HomeCase studiesA Connected Fuel Management and Tank Telemetry...

Case study · Hamburg, Germany

A Connected Fuel Management and Tank Telemetry Platform for Fleet Operators

Downstream fuel and fleet operations — on-site fuel dispensing, bulk storage tank management and fuel cost allocation

Fuel is one of the largest controllable costs in any fleet operation, and it is dispensed at an unattended pump, by a driver, into a vehicle, with nobody watching. Our team built a platform that turns the dispenser into a controlled, connected device: fuel does not flow until the platform has authorised the operator, the vehicle, the hose and the tank, and every unit dispensed is metered, priced, attributed and reconciled against measured tank inventory. It runs over a checksummed binary device protocol carried on MQTT, raw TCP and Bluetooth — the last of which lets the mobile application authorise and record a dispense with no network at all — behind a Java backend, a Vue 3 operations console and a React Native field application.

Industry
Downstream fuel and fleet operations — on-site fuel dispensing, bulk storage tank management and fuel cost allocation
Solution
An industrial IoT platform: a device-facing backend implementing a proprietary binary protocol over multiple transports, a REST API, a web operations and reporting console, and a mobile application used at the pump.
Platforms
Cross-platform · Web & API
Stack
Vue · React Native · React · TypeScript · MySQL · Redis
Location
Hamburg, Germany · delivered remotely
Role
Engineering, with the team behind for mobile and QA

Delivered remotely for a business based in Hamburg, Germany. Names withheld by agreement.

Project Overview

Industry: Downstream fuel and fleet operations — on-site fuel dispensing, bulk storage tank management and fuel cost allocation.

Type of solution: An industrial IoT platform: a device-facing backend implementing a proprietary binary protocol over multiple transports, a REST API, a web operations and reporting console, and a mobile application used at the pump.

Business context: Organisations that run vehicle fleets — construction, transport, agriculture, municipal services, mining — commonly store and dispense their own fuel. That arrangement saves money and creates a control problem. Consumption happens without supervision, tank contents are unknown between manual dips, meters drift out of calibration as they age, and the fuel eventually has to be charged back to a department, a vehicle or a job that nobody recorded at the time. The gap between fuel purchased and fuel accounted for is typically discovered long after anything can be done about it.

General users: Drivers and plant operators at the dispenser; fleet and fuel managers; inventory controllers; finance and reporting users; company administrators; resellers administering multiple client organisations; and platform administrators.

General purpose: To make every dispense an authorised, metered, attributed and reconcilable event — and to give operations a live view of tank contents, device health and consumption rather than a retrospective one.

The Business Challenge

Unattended dispensing is unaccountable dispensing. A pump that anyone can operate produces a fuel log that nobody can defend. Authorisation has to happen at the moment of dispense, at the device, or it does not happen at all.

Attribution has to be captured before the fuel flows. Odometer, engine hours, vehicle identity and job number are worth nothing if they are entered from memory at the end of a shift. They have to be a precondition of the pump turning on.

Fuel islands often have no signal. Depots, yards, quarries and remote sites are exactly the places where fuel is stored, and exactly the places where a connectivity-dependent system fails. The system had to work with no network and reconcile afterwards without creating duplicate records.

Tank contents are invisible between manual measurements. Without continuous level telemetry, tanks run dry or get topped up inefficiently, and there is no baseline against which dispensed volumes can be checked.

Instruments drift. Pulse meters and level sensors both lose accuracy over time. Without independent calibration of each, an inventory reconciliation reports a loss that is actually an instrument error — and the real losses hide inside the noise.

Three measurements of the same fuel never agree. Metered dispensing, tank level telemetry and recorded deliveries each tell a slightly different story. The value is not in forcing them to match; it is in presenting the differences clearly enough that someone can act.

Hardware fails mid-operation. A controller that loses power during a dispense leaves an open transaction and, potentially, an open valve. That is a safety issue before it is an accounting issue, and it cannot wait for someone to notice.

Fuel cost has to be sliced many ways. By vehicle, by department, by site, by operator, by job, by product, by period — and as efficiency, in distance or hours per unit of fuel. Reporting is not a reporting feature here; it is the reason the data is collected.

Our Approach

Make authorisation a precondition of physical actuation. Before any valve opens, the platform verifies the complete chain: that the operator is active and belongs to the company, that the department and site exist, that the hose and tank are active, that the product has a valid non-zero price, that the vehicle is active, that the vehicle is authorised on that hose, that the operator is authorised on that vehicle, and that the device is registered and reachable. Each check is separate and carries its own outcome, so a refused dispense always tells the operator exactly which condition failed rather than returning a generic error at a pump in the rain.

Treat pricing as part of the control path, not the reporting path. Fuel cannot be dispensed if there is no active price for that product. Costing is therefore never reconstructed retrospectively — every transaction carries the price that was in force at the moment it happened.

Build the device protocol as a first-class layer. Registration, heartbeat, configuration, valve control, mid-operation progress and completion frames are decoded through a dedicated codec with checksum verification, sequence numbering and command acknowledgement. Every frame is persisted with its raw payload alongside the decoded values, so a disputed transaction can be re-derived from exactly what the hardware sent.

Make offline a supported mode, not a degraded one. When there is no network, the mobile application connects to the dispenser controller directly over Bluetooth, runs the same command vocabulary, drives the dispense, tracks progress locally and buffers the resulting transactions — then uploads them in bulk when connectivity returns.

Calibrate the instruments independently and keep the history. Level sensors are calibrated by recording a sensor reading against a physical measurement and deriving a persistent offset. Meters are calibrated by recording indicated volume against actual measured volume and deriving a correction factor. Both are logged, so a change in reported inventory can always be separated from a change in instrumentation.

Compute volume from geometry. Tank contents are derived from measured level using the vessel's actual dimensions and shape, with configurable formats for non-standard tanks, rather than approximated from a generic table.

Watch for what did not happen. A scheduled job looks for transactions that were opened but never reported as complete, closes them, and raises an alert. Silence from a device is treated as an event in its own right.

Push everything slow off the critical path. Alarm and notification email is dispatched asynchronously, so SMTP latency can never delay the handling of a device frame or the response to a dispense command.

Store in UTC, present in the user's zone. Time zone conversion is applied declaratively at the serialisation boundary in both directions, so correctness across distributed sites is a property of the data model rather than a discipline applied endpoint by endpoint.

The Solution

Multi-level organisation model. Tenants, companies and departments, with a reseller tier able to administer multiple client organisations, and role-driven menus and permissions constructed server-side.

Physical asset registry. Sites, dispenser and hose points, storage tanks with geometry definitions, fuel products, suppliers, vehicles with odometer and engine-hour tracking, and devices with their metering constants and firmware attributes.

Device commissioning. Bulk identifier generation and activation tracking for new hardware, so devices are provisioned as a managed inventory rather than configured one at a time in the field.

Explicit authorisation graphs. Which operators may use which vehicles, which vehicles may draw from which hoses, which operators may access which tanks — all held as data, with a scheduled job that keeps blanket authorisations correct as assets are added and retired.

Controlled dispensing. Vehicle confirmation and reading capture at the pump, optional job-number entry, valve control, live progress during the dispense, and stop from either the application or the device itself.

Offline Bluetooth dispensing. Direct device control with local transaction buffering and deferred synchronisation, for sites with no usable connectivity.

Transaction ledger. Every dispense recorded with its full attribution — company, department, vehicle, hose, tank, site, product, operator, price, job number, readings, quantity and cost — plus its origin and edit history, and separate handling for on-site, off-site and manually entered records.

Tank inventory. Continuous level telemetry converted to volume, manual level entry, delivery and receipt recording with adjustments, totalizer and pump readings, refill thresholds with per-tank notification lists, and a stop-dispensing control.

Reconciliation reporting. Book-versus-measured balance, real-time balance and pump reconciliation, presenting the relationship between metered dispensing, measured inventory and recorded deliveries.

Fuel procurement. Supplier and product catalogue, orders raised against a tank with sequential numbering, delivery scheduling and templated order notifications.

Monitoring and alarms. A live device console showing status, heartbeat, battery, signal strength and decoded fault conditions, with historical logs, consumption and level dashboards, and email alerts for device faults, abnormal transactions and refill thresholds.

Calibration management. Independent level-sensor and meter calibration with full change history.

Reporting suite. Consumption and cost by date, operator, vehicle, site, site group, department and job number; cross-tabulated department-by-vehicle and operator-by-department views; fuel efficiency in distance or hours per unit; and billing summaries.

Data exchange. Spreadsheet import of vehicles, personnel, departments and transactions with downloadable templates and per-row error reporting, plus standard, customised and scheduled export configurations.

Public site and onboarding. A multi-language marketing and product site with a hardened enquiry and new-customer flow.

Key Features

Authorised dispensing at the device Fuel is released only after a complete chain of checks on operator, organisation, hose, tank, product, price, vehicle and device — each producing a specific, actionable outcome rather than a generic refusal.

Offline Bluetooth dispensing with deferred sync The mobile application drives the dispenser controller directly when there is no network, then uploads buffered transactions on reconnection — so remote sites are fully operational rather than degraded.

Checksummed binary device protocol over multiple transports Registration, configuration, control, progress and completion handled over cellular messaging, direct socket connections and Bluetooth, with acknowledgement, sequence tracking and raw payload retention for every frame.

Continuous tank level telemetry with geometric volume conversion Level readings converted to volume using each vessel's real dimensions and a calibrated offset, giving live inventory rather than periodic manual dips.

Independent meter and sensor calibration with audit history Two distinct calibration concepts, each logged, so instrumentation drift is separated from genuine inventory loss.

Inventory reconciliation across three sources Metered dispensing, measured tank level and recorded deliveries presented together with their differences, so discrepancies surface as something to investigate rather than as a year-end surprise.

Unclosed-transaction watchdog A scheduled job detects dispenses that were started but never reported complete, closes the record and raises an alert — treating device silence as an operational event.

Attribution captured before dispensing Vehicle identity, odometer or engine hours and job number are collected at the pump as a precondition of fuel flowing, so cost allocation is a by-product of the process rather than a later reconstruction.

Role-driven multi-tenant administration Tenant, company and department hierarchy with a reseller tier, and menus and actions constructed at runtime from the signed-in role's permissions.

Cost and efficiency reporting across every operational dimension Consumption and cost by vehicle, department, site, operator, job and period, with fuel efficiency and billing summaries derived from the same authoritative ledger.

Technical Architecture

Device edge. Dispenser controllers and tank level sensors communicate over cellular messaging to a broker, over a direct socket connection to a dedicated listener, or over Bluetooth to the mobile application. A shared codec decodes each frame type, verifies its checksum, updates device state, persists the raw and decoded record, returns an acknowledgement and fans the result out to live subscribers and alerting.

API layer. A token-authenticated REST API with controller trees partitioned by audience — administrative, monitoring and mobile — behind a global authentication interceptor with a narrow exclusion list, structured business error codes and generated interface documentation.

Business services layer. A service layer over a mapper-based persistence tier, with the dispense authorisation chain, the inventory and reconciliation calculations, the calibration derivations and the reporting aggregations as its principal concerns. Heavy aggregation is expressed as hand-written SQL rather than generated queries.

Real-time layer. Server-side WebSocket endpoints push device state and dispense progress to the operations console and to connected applications, keyed by channel type and user session.

Scheduling layer. Recurring jobs handle the unclosed-transaction watchdog and the synchronisation of blanket authorisation relationships as assets change.

Data layer. A relational database holding the organisational hierarchy, asset registry, authorisation graphs, transaction ledger, inventory movements, device report history and calibration logs, with logical deletion applied globally and all timestamps stored in UTC.

Caching layer. An in-memory store holds session tokens, refresh tokens, user context and external service tokens, keeping the application tier free of local session state.

Operations console. A component-based single-page application with a runtime-constructed route table driven by server-returned permissions, dense virtualised data grids for operational tables, charting for inventory and consumption, multi-language support and spreadsheet import/export flows.

Field application. A cross-platform mobile client combining a network path and a Bluetooth path over the same command vocabulary, with local transaction buffering, connectivity detection, push messaging and multi-language support.

Flow: Dispenser controllers and level sensors → binary protocol over messaging, socket or Bluetooth → codec and device state handling → authorisation, metering and inventory services → relational store → real-time push, alerting and reporting → operations console and field application

Technology Stack

Category Technology
Backend language Java
Backend framework Spring Boot
Database MySQL with MyBatis-Plus and XML-mapped queries
Caching & session store Redis
Device messaging MQTT via Spring Integration
Device socket transport Netty TCP server
Device protocol Custom binary frame codec with checksum verification and command acknowledgement
Real-time web delivery Server-side WebSocket endpoints
API authentication JWT with a server-side token registry and refresh tokens
API documentation Generated OpenAPI specifications
Reporting & data exchange Apache POI for spreadsheet import and export
Email Templated HTML notifications over SMTP; SendGrid with SMTP fallback on the public site
Observability Log4j2 with rolling retention; automatic slow-query detection on the data source
Scheduling Framework-native scheduled jobs with a configured async thread pool
Admin console Vue 3 with TypeScript, Vite, Element Plus, Pinia
Admin data & charts Virtualised data grid components, ECharts visualisation
Admin tooling ESLint, Prettier, Stylelint, Husky, lint-staged, conventional commits
Mobile framework React Native with TypeScript and React Navigation
Mobile device access Bluetooth Low Energy client with a bundled protocol UUID reference set
Mobile services Firebase authentication and push messaging, connectivity detection, local storage
Public site Vue with a hardened Node service: captcha, per-route rate limiting, process management
Deployment Capistrano release and rollback pipelines onto systemd services, with staging and production stages; container-based front-end builds

Technical Challenges & Solutions

Challenge Our Approach
Fuel must not flow to an unauthorised operator, vehicle or hose A complete pre-dispense authorisation chain evaluated as a sequence of independent checks across operator, organisation, asset, product, price, vehicle and device — each with a distinct outcome, so the person at the pump is told precisely what is wrong instead of receiving a generic failure.
Fuel islands frequently have no network connectivity Bluetooth built as a first-class dispensing path rather than a fallback: the mobile application drives the controller directly using the same command vocabulary, tracks progress locally, buffers transactions and synchronises in bulk on reconnection.
The same device commands must work over three different transports A shared frame codec with checksum verification, sequence numbering and acknowledgement handling, implemented once server-side for the networked transports and mirrored in the mobile client for the direct path, so behaviour is consistent regardless of how a device is reached.
Meters and level sensors drift, hiding real losses inside instrument error Two independent calibration mechanisms — a level-sensor offset derived from reading versus physical measurement, and a meter correction factor derived from indicated versus actual volume — each with a full change history, so instrumentation and inventory can always be separated.
Metered dispensing, tank telemetry and delivery records never agree exactly Reconciliation reporting that presents book balance, real-time balance and pump reconciliation side by side with their differences, so discrepancies become an operational signal rather than a year-end discovery.
Hardware can fail mid-dispense, leaving an open transaction A short-interval watchdog that identifies dispenses started but never reported complete, corrects the record and raises an alert — treating device silence as an event rather than as missing data.
Fuel cost must be analysed across many independent dimensions A fully attributed transaction ledger capturing organisation, asset, operator, vehicle, product, price and job at the moment of dispense, with aggregation pushed into hand-written SQL where the reporting shapes are heavy.
Notification volume must never delay device handling Alarm, order and threshold notifications dispatched asynchronously on a dedicated thread pool, so mail delivery latency cannot block frame processing or dispense control.
Distributed sites operating across time zones UTC storage with declarative conversion applied at the serialisation boundary in both directions, making time zone correctness a property of the model rather than a per-endpoint discipline.

Security & Reliability

Token authentication with central revocation. Sessions are authenticated by signed token and validated against a server-side registry, so access can be withdrawn centrally rather than waiting for a token to expire. Refresh tokens are issued and rotated for the mobile client.

Session invalidation on credential and status change. A password change or an account deactivation invalidates existing sessions at the next request, enforced in the authentication interceptor rather than left to individual endpoints.

Authorisation as data, enforced server-side. Which operators may use which vehicles and hoses, and which tanks they may access, is held as explicit relationships. Menu structure and available actions are constructed from the signed-in role's permissions on the server, not assumed by the client.

Physical actuation behind a complete check chain. Because a dispense is a real-world side effect, every precondition is asserted before a valve is opened, and each failure is distinguishable.

Full device provenance. Every decoded device frame is retained with its raw payload, so any transaction can be re-derived from what the hardware actually transmitted — the basis for resolving a disputed dispense.

Calibration audit trail. Changes to sensor offsets and meter factors are logged with who and when, so a shift in reported inventory can always be attributed.

Failure detection rather than failure assumption. Incomplete dispenses, device faults and threshold breaches are detected and alerted, with fault conditions decoded into readable status rather than stored opaquely.

Hardened public entry points. The public-facing enquiry and onboarding forms apply captcha verification and per-route rate limiting.

Operational observability. Structured logging with size and time-based rotation and retention policies, and automatic detection of queries exceeding a fixed latency threshold.

Atomic releases with rollback. Deployment uses versioned releases with retained history and a single-command rollback path across staging and production stages.

Scalability & Performance

Stateless application tier. Session state, user context and external service tokens live in an in-memory store rather than in the application instance, so instances can be added or replaced without affecting signed-in users.

Device traffic decoupled from user traffic. Device ingestion arrives on its own transports, so a surge in telemetry does not compete with interactive requests, and broker capacity becomes an independent scaling lever.

Aggregation pushed to the database. Report queries that touch large volumes of transaction history are written as explicit SQL rather than generated by the ORM, keeping the aggregation where it belongs and the result sets bounded by pagination.

Automatic slow-query detection. The data source is instrumented so that queries exceeding a fixed threshold are surfaced by default, rather than being discovered by users.

Asynchronous side effects. Notification and alerting work is dispatched to a configured thread pool, so the latency of external mail delivery never appears in a device or user response.

Cached authentication path. Session validation resolves from the in-memory store, avoiding a database round trip on every authenticated request.

Bounded scheduled work. Recurring jobs operate over explicit query windows rather than scanning full history.

Client-side efficiency. The operations console uses virtualised grid components for dense operational tables and constructs only the routes a role can access; the mobile application polls dispense state on a short fixed interval only while a dispense is active.

Business Outcomes

  • Dispensing became a controlled event. Fuel is released only against a verified operator, vehicle, hose, tank and price, and a refusal explains itself at the pump.
  • Attribution is captured at the point of use. Vehicle, readings and job number are collected before fuel flows, so cost allocation is a by-product of dispensing rather than a monthly reconstruction.
  • Remote sites operate at full capability. Bluetooth dispensing with deferred synchronisation means no connectivity does not mean no fuel — and no missing records.
  • Tank contents are known continuously. Level telemetry converted through vessel geometry replaces periodic manual measurement, supporting both refill planning and reconciliation.
  • Instrument drift is separated from inventory loss. Independent, audited calibration of meters and sensors means a reconciliation discrepancy points at something real.
  • Discrepancies surface as operational signals. Metered dispensing, measured inventory and recorded deliveries are presented together with their differences, when they can still be acted on.
  • Incomplete operations are caught automatically. A dispense that never reports completion is detected, closed and alerted rather than sitting open in the ledger.
  • Fuel cost is analysable across every dimension the business cares about — vehicle, department, site, operator, job, product and period, including efficiency measures, all derived from one authoritative record.
  • Device health is visible. Live status, connectivity, battery, signal and decoded fault conditions turn hardware maintenance into something planned rather than reactive.

Why it worked

Software that controls physical equipment is a different discipline from software that records what happened. A failed API call can be retried; a valve that opens when it should not have has already dispensed the fuel. That difference shows up everywhere in a platform like this — in how authorisation is structured, in how commands are acknowledged, in what happens when a device goes quiet mid-operation, and in whether the system is honest about the difference between what it measured and what it believes.

Our team builds for that reality. We treated the device protocol as a first-class layer with checksum verification, acknowledgement and full raw-payload retention, because when a transaction is disputed the only credible answer is what the hardware actually sent. We made offline operation a supported mode rather than a degradation, because the places fuel is stored are the places connectivity fails. We built two independent calibration mechanisms with audit history, because a reconciliation system that cannot separate instrument drift from real loss is worse than none. And we made the platform notice what did not happen — the dispense that never closed, the device that stopped reporting — because in industrial systems, silence is data.

Our teams work across Java and Spring, industrial device protocols and IoT messaging, Bluetooth and embedded integration, real-time web delivery, modern TypeScript front ends, cross-platform mobile development, and the reporting and reconciliation logic that operational businesses actually run on — with the domain judgement to know which parts of a physical process must be modelled exactly and which can be simplified.

Final Summary

A fleet operator with its own fuel storage has a control problem disguised as a convenience. Fuel is dispensed at an unattended pump, by whoever turns up, into whatever is parked there, and the record of what happened is a clipboard. Tank contents are unknown between manual measurements. Meters drift. Deliveries, dispensing and stock levels tell three different stories, and the differences are only discovered when it is far too late to investigate them.

Our team built a platform that closes that loop at the point where it opens. The dispenser is a connected, controlled device: it will not release fuel until the platform has verified the operator, their authorisation on the vehicle, the vehicle's authorisation on the hose, the tank, the product and its current price — and it captures the vehicle identity and readings as a precondition, not a formality. Every dispense is metered by the device, reported over a checksummed binary protocol, retained with its raw payload, priced at the rate in force and attributed to a department, a vehicle, an operator and a job. When there is no network, the mobile application drives the controller directly over Bluetooth and synchronises afterwards, so a remote site loses connectivity without losing either fuel or records.

Around that sits the machinery an operational business needs to trust the numbers: continuous tank level telemetry converted through vessel geometry, independently calibrated meters and sensors with audit history, reconciliation reporting that shows metered dispensing against measured inventory and recorded deliveries, a watchdog that catches dispenses no device ever finished, live device health with decoded fault conditions, and a reporting suite that slices fuel cost and efficiency every way the business is actually managed. Delivered as a Java backend speaking a multi-transport device protocol, a role-driven Vue 3 operations console and a React Native application built for the pump — it replaces a clipboard with an accountable record, which in fuel management is the whole game.

01 — Questions

asked about this kind of project

How is building software that controls hardware different from building a normal web platform?

The difference is that side effects are irreversible. A failed request can be retried; a valve that opened cannot be un-opened. That changes the design in specific ways: authorisation must be complete before actuation rather than checked afterwards, commands must be acknowledged rather than assumed delivered, and the system must have an answer for what happens when a device stops responding mid-operation. Most of the engineering effort goes into the failure paths, not the happy one.

How do you make an industrial mobile application work without connectivity?

By making the offline path a real path rather than a cached copy of the online one. In this platform the application talks to the device controller directly over Bluetooth using the same command vocabulary the server uses over the network, drives the full operation locally, buffers the resulting records, and synchronises in bulk when a connection returns. The critical design point is idempotent upload — a deferred batch must not create duplicates when it is retried.

Why keep raw device payloads as well as decoded values?

Because eventually someone disputes a transaction. If the system has only the decoded result, the argument is unresolvable. If it has the raw frame alongside the decoding, the transaction can be re-derived and the question settled. The storage cost is trivial next to the cost of not being able to answer.

How do you handle sensor and meter drift in an inventory system?

By calibrating each instrument independently and keeping the history. Level sensors are calibrated against a physical measurement to produce an offset; meters are calibrated against an actual measured volume to produce a correction factor. Both are logged with who changed them and when. Without this, a reconciliation report cannot distinguish a genuine loss from an ageing instrument — and that ambiguity makes the whole reconciliation worthless.

What does inventory reconciliation actually involve?

Comparing what the meters say was dispensed, what the level telemetry says is in the tank, and what the records say was delivered. These three will never agree exactly, and the goal is not to force them to. It is to present the differences promptly and clearly enough that someone can investigate while the trail is warm, rather than discovering an unexplained gap at year end.

Should device messaging go over MQTT, a raw socket, or Bluetooth?

It depends on the device, and in practice a mature platform supports more than one. Cellular devices suit a broker-based protocol; some hardware only speaks a raw socket; local control needs Bluetooth. The important decision is to define the command and telemetry vocabulary once and implement transports against it, rather than letting each transport grow its own dialect — that divergence is what makes these systems unmaintainable.

How do you design authorisation for a system that controls physical equipment?

As an explicit chain of individual checks, each with its own outcome. It is tempting to collapse them into a single permission query, but the person affected is standing at a machine that will not start, and "not authorised" is not an actionable message. Separating the checks costs a little verbosity and returns a system that can tell someone exactly which condition to fix.

What should be built first in an industrial IoT platform?

The device layer and its data retention — protocol handling, acknowledgement, state tracking and raw payload storage — before the dashboards that sit on top. It is the least visible part of the system and the part everything else depends on. Reporting built over unreliable device data produces confident, wrong answers, and confident wrong answers are worse than no answers at all.

03 — Similar project?

describe what is different about yours

Build yours.

Tell me what exists, what you need and the deadline. Fixed price for a defined scope, or hourly from $10 with a written estimate first.

Ahmedabad, India · IST (UTC+5:30) · --:-- IST · Mon–Fri 09:00–18:00 IST · US & EU overlap daily

No newsletter, no CRM. Just a reply.