diff --git a/src/controllers/staging.controller.js b/src/controllers/staging.controller.js
index e1038d8a..69d8da11 100644
--- a/src/controllers/staging.controller.js
+++ b/src/controllers/staging.controller.js
@@ -71,6 +71,25 @@ export const findAll = async (req, res) => {
   }
 };
 
+export const generateOfferFile = async (req, res) => {
+  try {
+    await assertIfReadOnlyMode();
+    await assertStagingTableNotEmpty();
+    await assertHomeOrgExists();
+    await assertWalletIsSynced();
+    await assertNoPendingCommits();
+
+    const offerFile = await Staging.generateOfferFile();
+    res.json(offerFile);
+  } catch (error) {
+    console.trace(error);
+    res.status(400).json({
+      message: 'Error generating offer file.',
+      error: error.message,
+    });
+  }
+};
+
 export const commit = async (req, res) => {
   try {
     await assertIfReadOnlyMode();
diff --git a/src/datalayer/persistance.js b/src/datalayer/persistance.js
index 03eec528..7ede30a2 100644
--- a/src/datalayer/persistance.js
+++ b/src/datalayer/persistance.js
@@ -437,4 +437,28 @@ const getMirrors = async (storeId) => {
   }
 };
 
+export const makeOffer = async (offer) => {
+  const options = {
+    url: `${rpcUrl}/make_offer `,
+    body: JSON.stringify(offer),
+  };
+
+  try {
+    const response = await request(
+      Object.assign({}, getBaseOptions(), options),
+    );
+
+    const data = JSON.parse(response);
+
+    if (data.success) {
+      return data;
+    }
+
+    throw new Error(data.error);
+  } catch (error) {
+    console.log(error);
+    throw error;
+  }
+};
+
 export const addMirror = _addMirror;
diff --git a/src/models/staging/staging.model.js b/src/models/staging/staging.model.js
index a28a3411..2c815c4e 100644
--- a/src/models/staging/staging.model.js
+++ b/src/models/staging/staging.model.js
@@ -2,18 +2,26 @@
 
 import _ from 'lodash';
 import Sequelize from 'sequelize';
+const Op = Sequelize.Op;
+
 const { Model } = Sequelize;
 import { Project, Unit, Organization, Issuance } from '../../models';
-import { encodeHex } from '../../utils/datalayer-utils';
+import { encodeHex, generateOffer } from '../../utils/datalayer-utils';
 
 import * as rxjs from 'rxjs';
 import { sequelize } from '../../database';
 
 import datalayer from '../../datalayer';
+import { makeOffer } from '../../datalayer/persistance';
 
 import ModelTypes from './staging.modeltypes.cjs';
 import { formatModelAssociationName } from '../../utils/model-utils.js';
 
+import {
+  createXlsFromSequelizeResults,
+  transformFullXslsToChangeList,
+} from '../../utils/xls';
+
 class Staging extends Model {
   static changes = new rxjs.Subject();
 
@@ -32,6 +40,174 @@ class Staging extends Model {
     return super.upsert(values, options);
   }
 
+  static generateOfferFile = async () => {
+    const stagingRecord = await Staging.findOne({
+      // where: { isTransfer: true },
+      where: { commited: false },
+      raw: true,
+    });
+
+    const takerProjectRecord = _.head(JSON.parse(stagingRecord.data));
+
+    const myOrganization = await Organization.findOne({
+      where: { isHome: true },
+      raw: true,
+    });
+
+    const maker = { inclusions: [] };
+    const taker = { inclusions: [] };
+
+    // The record still has the orgUid of the takerProjectRecord,
+    // we will update this to the correct orgUId later
+    maker.storeId = takerProjectRecord.orgUid;
+    taker.storeId = myOrganization.orgUid;
+
+    const makerProjectRecord = await Project.findOne({
+      where: { warehouseProjectId: takerProjectRecord.warehouseProjectId },
+      include: Project.getAssociatedModels().map((association) => {
+        return {
+          model: association.model,
+          as: formatModelAssociationName(association),
+        };
+      }),
+    });
+
+    makerProjectRecord.projectStatus = 'Transitioned';
+
+    const issuanceIds = makerProjectRecord.issuances.reduce((ids, issuance) => {
+      if (!ids.includes(issuance.id)) {
+        ids.push(issuance.id);
+      }
+      return ids;
+    }, []);
+
+    let unitMakerRecords = await Unit.findAll({
+      where: {
+        issuanceId: { [Op.in]: issuanceIds },
+      },
+      raw: true,
+    });
+
+    // Takers get an unlatered copy of all the project units from the maker
+    const unitTakerRecords = _.cloneDeep(unitMakerRecords);
+
+    unitMakerRecords = unitMakerRecords.map((record) => {
+      record.unitStatus = 'Exported';
+      return record;
+    });
+
+    const primaryProjectKeyMap = {
+      project: 'warehouseProjectId',
+      projectLocations: 'id',
+      labels: 'id',
+      issuances: 'id',
+      coBenefits: 'id',
+      relatedProjects: 'id',
+      estimations: 'id',
+      projectRatings: 'id',
+    };
+
+    const primaryUnitKeyMap = {
+      unit: 'warehouseUnitId',
+      labels: 'id',
+      label_units: 'id',
+      issuances: 'id',
+    };
+
+    const makerProjectXslsSheets = createXlsFromSequelizeResults({
+      rows: [makerProjectRecord],
+      model: Project,
+      toStructuredCsv: true,
+    });
+
+    const takerProjectXslsSheets = createXlsFromSequelizeResults({
+      rows: [takerProjectRecord],
+      model: Project,
+      toStructuredCsv: true,
+    });
+
+    const makerUnitXslsSheets = createXlsFromSequelizeResults({
+      rows: unitMakerRecords,
+      model: Unit,
+      toStructuredCsv: true,
+    });
+
+    const takerUnitXslsSheets = createXlsFromSequelizeResults({
+      rows: unitTakerRecords,
+      model: Unit,
+      toStructuredCsv: true,
+    });
+
+    const takerProjectInclusions = await transformFullXslsToChangeList(
+      takerProjectXslsSheets,
+      'insert',
+      primaryProjectKeyMap,
+    );
+
+    const makerProjectInclusions = await transformFullXslsToChangeList(
+      makerProjectXslsSheets,
+      'insert',
+      primaryProjectKeyMap,
+    );
+
+    const makerUnitInclusions = await transformFullXslsToChangeList(
+      makerUnitXslsSheets,
+      'insert',
+      primaryUnitKeyMap,
+    );
+
+    const takerUnitInclusions = await transformFullXslsToChangeList(
+      takerUnitXslsSheets,
+      'insert',
+      primaryUnitKeyMap,
+    );
+
+    /* Object.keys(maker.inclusions).forEach((table) => {
+      maker.inclusions[table] = maker.inclusions[table]
+        .filter((inclusion) => inclusion.action !== 'delete')
+        .map((inclusion) => ({ key: inclusion.key, value: inclusion.value }));
+    });*/
+
+    maker.inclusions.push(
+      ...makerProjectInclusions.project
+        .filter((inclusion) => inclusion.action !== 'delete')
+        .map((inclusion) => ({
+          key: inclusion.key,
+          value: inclusion.value,
+        })),
+    );
+
+    maker.inclusions.push(
+      ...makerUnitInclusions.unit
+        .filter((inclusion) => inclusion.action !== 'delete')
+        .map((inclusion) => ({
+          key: inclusion.key,
+          value: inclusion.value,
+        })),
+    );
+
+    taker.inclusions.push(
+      ...takerProjectInclusions.project
+        .filter((inclusion) => inclusion.action !== 'delete')
+        .map((inclusion) => ({
+          key: inclusion.key,
+          value: inclusion.value,
+        })),
+    );
+
+    taker.inclusions.push(
+      ...takerUnitInclusions.unit
+        .filter((inclusion) => inclusion.action !== 'delete')
+        .map((inclusion) => ({
+          key: inclusion.key,
+          value: inclusion.value,
+        })),
+    );
+
+    const offer = generateOffer(maker, taker);
+    return makeOffer(offer);
+  };
+
   // If the record was commited but the diff.original is null
   // that means that the original record no longer exists and
   // the staging record should be cleaned up.
diff --git a/src/routes/v1/resources/staging.js b/src/routes/v1/resources/staging.js
index b17a08c1..9e3cbc52 100644
--- a/src/routes/v1/resources/staging.js
+++ b/src/routes/v1/resources/staging.js
@@ -18,6 +18,10 @@ StagingRouter.get('/', validator.query(stagingGetQuerySchema), (req, res) => {
   return StagingController.findAll(req, res);
 });
 
+StagingRouter.get('/offer', (req, res) => {
+  return StagingController.generateOfferFile(req, res);
+});
+
 StagingRouter.put('/', (req, res) => {
   return StagingController.editRecord(req, res);
 });
diff --git a/src/utils/datalayer-utils.js b/src/utils/datalayer-utils.js
index 90df64f8..dbd30b55 100644
--- a/src/utils/datalayer-utils.js
+++ b/src/utils/datalayer-utils.js
@@ -31,3 +31,21 @@ export const keyValueToChangeList = (key, value, includeDelete) => {
 
   return changeList;
 };
+
+export const generateOffer = (maker, taker) => {
+  return {
+    maker: [
+      {
+        store_id: maker.storeId,
+        inclusions: maker.inclusions,
+      },
+    ],
+    taker: [
+      {
+        store_id: taker.storeId,
+        inclusions: taker.inclusions,
+      },
+    ],
+    fee: 0,
+  };
+};