- Signing a property organisation is not the same as making it operational.
- Before its first useful purchase, the platform needs to understand where goods are consumed, which building or unit receives them and which general-ledger account should carry the cost.
- The tempting implementation is a one-off script: load the files, insert records and fix whatever breaks manually.
- For a property-procurement marketplace built with Medusa, we created a tracked import journey for properties, units and GL accounts.
The client problem
Signing a property organisation is not the same as making it operational.
Before its first useful purchase, the platform needs to understand where goods are consumed, which building or unit receives them and which general-ledger account should carry the cost. That information rarely arrives through an API. It arrives as spreadsheets maintained by finance and operations, with familiar column names, duplicate rows and references that may use either an internal ID or a property name.
The tempting implementation is a one-off script: load the files, insert records and fix whatever breaks manually. That gets one client through onboarding and leaves no product capability for the next one.
For a property-procurement marketplace built with Medusa, we created a tracked import journey for properties, units and GL accounts. Each file enters through an organisation-owned upload session, becomes a durable import record, stages every row and processes it according to the entity's actual business rules.
The result is not merely “CSV imported”. The organisation emerges with the dimensions the rest of purchasing, approvals, allocations and accounting need.
Onboarding data has an order even when the files do not
The three datasets are related.
Properties establish the operating sites. Units belong to those properties. GL accounts belong to the organisation and later receive purchase allocations. Importing units before their property exists creates unresolved references. Treating all three files as generic rows loses the different rules that make each entity useful.
We gave each import an explicit entity type: property, unit or GL account. That type determines the upload purpose, parser, validation rules and processor. It also prevents two active imports for the same organisation and entity type from running over each other.
This produces a clear onboarding sequence without hard-coding a single giant migration. Properties can be loaded first, units can resolve against the resulting property set and finance can maintain the chart of accounts independently.
The system remains modular while the operator understands the dependency: a unit needs a known property; a GL code needs an organisation; a property needs enough address information to participate in commerce.
Start from organisation ownership
An onboarding file can contain sensitive operational and accounting structure.
The import route requires an authenticated customer with an organisation context. It retrieves the uploaded session and checks both uploader customer and uploader organisation. A foreign or missing session returns not found rather than confirming another tenant's operation.
Purpose must also match the selected entity type. A file uploaded for unit import cannot be submitted as a GL-account import merely by changing a body field. The session must already be in the uploaded state.
These checks make the import an organisation-scoped business action. The browser does not choose an arbitrary organisation ID and the workflow does not infer tenancy from CSV content.
Only after ownership, purpose and state pass does the platform create the import record and move the upload into ingestion.
Return quickly, then track the real work
Organisation files can contain enough rows that processing them inside one HTTP request is fragile.
The route creates a durable import record with filename, organisation, actor, entity type, upload session, start time and counters. It then launches a tracked Medusa workflow and returns HTTP 202 with an import ID, transaction ID and status URL.
The upload-session metadata begins with a staging progress shape: phase, processed count, total, percentage and update timestamp. The background workflow can update that state while the user continues working.
This is operationally different from “fire and forget”. The caller receives a stable identifier immediately. The backend has a record to complete or fail. Support can inspect which organisation, actor, file and workflow belong together.
Asynchronous onboarding becomes a product journey rather than a long request whose browser timeout hides the outcome.
Stage every row before changing the organisation
The workflow separates reading from application.
The staging step streams the CSV and normalises column names. Empty rows are ignored. Each non-empty row receives a stable row index, raw source payload, derived entity key, parsed payload, initial status and any validation error.
Rows are inserted into a dedicated staging table in batches. Missing required fields become skipped rows with explicit reasons instead of crashing the entire file or disappearing from the count.
This creates an evidence layer between the spreadsheet and the domain models. The platform can explain that row 17 lacked a postcode or that row 42 referenced an unknown property. It can also calculate total, processed and failed counters from durable data.
Most importantly, parsing the file is not the same operation as creating business entities. The system knows what it intends to do before it begins applying rows.
Turn a property row into commerce context
A property needs more than a name.
The parser accepts familiar aliases for name, address lines, city, state and ZIP code. Those core fields are required. Optional external ID, type and unit count are retained when valid. Structured address data is preserved in metadata while searchable country, province, city and postal fields are materialised on the property.
During processing, existing properties are loaded only for the active organisation. Names are compared case-insensitively, including duplicates encountered earlier in the same file. Duplicate rows are skipped rather than creating two operating sites with nearly identical names.
New properties are created as active and linked to the organisation. Existing organisation masters are then linked to the created sites so they can operate across the portfolio according to the application's access model.
One spreadsheet row therefore becomes a usable commerce context: an owned property with structured geography and the right organisational relationships.
Resolve a unit by property ID or property name
Unit files often come from operational systems whose users know property names better than platform IDs.
The parser accepts a property identifier, unit number and optional building, description and floorplan fields. Processing loads properties belonging to the organisation and indexes them by both ID and lower-cased name.
If the referenced property cannot be found, the row is skipped and records a row-specific error. The workflow does not create an orphan unit or silently attach it to a similarly named property from another organisation.
Duplicate detection uses a composite of property, building name and unit number. This reflects the business reality that “Unit 101” can exist in several buildings or properties while still preventing the same unit from being created twice within its actual location.
The importer gives operators forgiving input vocabulary without weakening the domain relationship every unit must satisfy.
Treat GL codes differently from physical entities
General-ledger accounts require update semantics, not only duplicate skipping.
A property row with a duplicate name should not casually overwrite address data. A unit row with the same composite key should not be recreated. But finance may intentionally send an existing GL code with an updated label.
The GL processor loads accounts for the organisation and indexes them by case-insensitive code. Rows in the same CSV are also grouped by normalised code, with the final row providing deterministic replacement values. Existing codes are updated; new codes are created.
This allows the chart of accounts to evolve while retaining the code that downstream budgets and allocations reference. A file does not need separate “create” and “rename” modes to keep descriptions current.
The distinction is an important product lesson: one generic duplicate policy does not fit every onboarding entity. Business identity determines whether a repeated key means reject, skip or update.
Process bounded batches instead of loading the file forever
After staging, the processor reads pending rows in ordered batches.
It applies the entity-specific operation, marks rows processed or skipped and advances counters. It does not require the complete CSV and every existing domain record to remain as one expanding in-memory structure for the entire workflow.
Property and unit processors load the relevant organisation context needed for duplicate and relationship checks. Staged rows provide the bounded work queue. Database status identifies what remains pending.
This architecture makes progress and failure easier to reason about. If processing stops, durable row states show which work completed and which rows remain. The import record can retain accumulated processing errors rather than returning one generic server exception.
The bounded workflow and its counters make the operational benefit visible: the team can see how much work entered the system, how much completed and exactly where intervention is needed.
Let imperfect files finish usefully
Onboarding should not force a choice between accepting every bad row and rejecting every good one.
Missing required fields are skipped during staging. Duplicate properties or units are skipped during processing. Units with unresolved properties create specific errors. Valid rows can still progress.
At the end, the import record distinguishes processed and failed or skipped counts and retains structured processing errors. The upload session also carries progress and final workflow state for the user-facing status view.
This gives the onboarding team a practical loop: import the clean majority, inspect the exact rejected rows, correct the source file and submit a focused follow-up. They do not have to compare the entire spreadsheet with the database to discover where execution stopped.
Partial usefulness does not mean silent partial success. The row ledger and counters make the boundary visible.
Connect onboarding to what buyers do next
Properties, units and GL accounts are not administrative decoration.
Properties define buyer context, permissions, delivery geography, budgets and reporting. Units let purchased quantities be allocated to the places that consume them. GL accounts connect the basket and order to spend controls and accounting exports.
That is why we modelled onboarding inside the commerce platform rather than as an external data-load exercise. The entities created here become the vocabulary used by approvals, purchase history and finance later.
An organisation is ready to buy when those relationships are trustworthy enough for the next workflow to depend on them.
The import capability shortens the path from existing client data to that operable state while preserving the controls the rest of the marketplace expects.
Why Medusa was the right foundation
Medusa supplied authenticated customer flows, workflow orchestration and the order lifecycle the new organisation would eventually use. The project needed property, unit and GL modules around that engine, plus an onboarding route capable of materialising them from real client data.
We extended the platform with durable upload and import modules, staged-row processing and entity-specific rules. Organisation context governs access. Medusa workflows coordinate asynchronous work. Domain services create the records that purchasing features consume.
This avoided a separate onboarding application that would later need to reproduce commerce identities and permissions. The same platform that imports a property can immediately use it for buyer context, fulfilment, allocation and reporting.
The architecture turns client spreadsheets into native commerce capabilities rather than leaving them as a parallel source of truth.
A practical onboarding-import checklist
Before importing organisational structure, define:
- Which actor and organisation own the upload?
- Does each entity type have an exact upload purpose?
- Can two imports of the same type run concurrently?
- Is a durable import record created before background work starts?
- Are raw and parsed row values retained separately?
- Which fields are required for a property?
- How are address components materialised?
- What uniquely identifies a duplicate property?
- Can units resolve a property by both ID and name?
- What composite key identifies a unit?
- What happens when its property is unknown?
- Should a repeated GL code be skipped or updated?
- How are duplicate codes inside one file resolved deterministically?
- Are rows processed in bounded batches?
- Can valid rows complete when another row is invalid?
- Where can operators inspect row errors and progress?
- How do the imported entities participate in purchasing next?
The answers determine whether onboarding creates trustworthy structure or merely populates tables.
The broader lesson
Enterprise onboarding is part of the product.
The client already has properties, units and accounting codes. The challenge is not inventing that data; it is admitting it into a new commerce model without losing ownership, relationships or explainability.
For this marketplace, each file becomes an organisation-scoped tracked import. Rows are staged before mutation. Properties receive structured geography and master access. Units must resolve to an owned property and remain unique within building context. GL accounts are upserted by code so finance can update names. Invalid rows stay visible while valid work progresses.
The organisation moves from spreadsheets to an operable buying structure through a repeatable workflow—not a private script somebody is afraid to run twice.
That is the real onboarding capability: turning existing business data into relationships the commerce platform can trust on the first order and every order after it.
