Data migration brings your existing business data into Off The Couch from a previous system. Each migration type has its own CSV schema with required and optional columns. Prepare the files according to the schemas in this article, then send them to your Off The Couch migration administrator.
Getting started
Navigate to Settings > Data migration from the sidebar.
How it works
Migrations are performed by Off The Couch administrators only. The page shows a notice at the top of the Migration upload tab:
Currently Migrations can only be performed by Off The Couch Administrators. Please contact Nick at nick@offthecouch.io or on Discord to schedule your migration. In preparation please look at Migration Advice to the right and the Migration Types below to prepare reports in advance.
The CSV upload control is hidden for non-OTC accounts. Your job is to prepare CSV files using the schemas below and send them along; Nick (or another Off The Couch admin) runs the actual import on your behalf and the results show up in the migration log on this page.
The page has two tabs:
| Tab | Purpose |
|---|---|
| Migration upload | The migration type picker, schema panel, and (for OTC admins) the upload control. The right side shows the Migration Advice panel |
| Migration log | One row per past migration run, with success / failure / duplicate counts and per-run actions (view records, view duplicates, download error records, purge records) |
Migration Advice
The page surfaces four pieces of generic advice on the right side of the Migration upload tab. They apply to every migration:
- Recommended order of file uploads: 1. Customers (Global), 2. Transactions (By Group / Location), 3. Bookings (By Group / Location, needs to be associated to a transaction by an ID), 4. Waivers (By Group / Location), 5. Gift cards, 6. Customer credit (Global), 7. Promo codes (By Group / Location).
- Make sure your headers match the ones provided. Use the copy tool on the page (or the schemas in this article) to avoid typos.
- Headers marked with
*are required. Every other column is optional. - Make sure you have corresponding events created before uploading bookings or waivers and that the names match.
Headers are case-sensitive. "Email" works; "email" or "EMAIL" doesn't. Match the casing exactly as shown in this article.
Step-by-step guide
Prepare your files
- Look at the Migration Advice above and follow the recommended order
- Pick the migration type you're starting with from the schemas below
- Build a CSV with the headers from the schema. Use the copy icon on the Settings > Data migration page to copy the field list and avoid typos
- Make sure required fields (marked with
*) have a value in every row - Confirm your event names in Events > Event settings match the names in Booking name and waiver columns (corresponding events must exist before bookings or waivers can be migrated)
Send your files for migration
- Once your CSVs are ready, contact Nick at nick@offthecouch.io or on Discord (per the notice on the page)
- Share your prepared CSV files with him
- Off The Couch administrators run the import on your behalf
- The result lands in the Migration log tab on this page
Review the migration log
Open the Migration log tab on Settings > Data migration. Each previous migration run is one row with these columns:
| Column | What it shows |
|---|---|
| Upload type | Migration type as a badge |
| Uploaded by | Team member who started the run, plus the timestamp |
| Records imported | Successful rows imported (e.g., "150 / 200") with a progress bar while the import is in flight. Shows a purged badge once the run is purged |
| Failed records | Rows that didn't import due to validation errors |
| Duplicates | Rows skipped because they matched existing records |
| Actions | Three-dot menu with View records, View duplicates, Download error records, Purge records |
Use the per-page selector at the top right (5, 10, 25, or 50) to change rows per page. The empty state ("No data has been uploaded yet.") appears when no migrations have been run.
Inspect a previous run
- In the migration log, open the three-dot menu on the run
- Pick the action you need:
| Action | When |
|---|---|
| View records | See the records imported by the run |
| View duplicates | See the rows that were skipped because they matched existing records (only present when duplicates were found) |
| Download error records | Get a CSV of the failed rows with their error reason. Fix and resend (only present when there were failures) |
| Purge records | Permanently remove every record imported by this specific run. Requires the Delete migrations permission |
Purge a migration run
- In the migration log, open the three-dot menu on the run you want to remove
- Click Purge records
- Confirm the Are you sure? prompt
- Every record imported by that specific run is deleted
Purging is permanent. Records that already existed before the run are not affected.
Schemas
Customers (Global)
Customer records. Migrated once per account, not per group.
| Field | Required | Description |
|---|---|---|
| First name | No | Customer's first name |
| Last name | No | Customer's last name |
| Yes | Used as the unique customer identifier | |
| Birthday | No | Date of birth in YYYY-MM-DD format |
| Phone | No | Phone number |
| Address line 1 | No | Street address |
| Address line 2 | No | Apartment, suite, or unit |
| City | No | City |
| State | No | State or province |
| Zip | No | ZIP or postal code |
| Country | No | Two-letter ISO 3166-1 alpha-2 code (US, CA, GB, AU, DE, NZ). The page has a Download reference link for the full country code list |
| Email opt out | No | Whether the customer has opted out of marketing emails. Boolean: 1 / 0 or true / false |
| Photo opt out | No | Whether the customer has opted out of photo sharing. Boolean: 1 / 0 or true / false |
| Date joined | No | When the customer first interacted with your business, in YYYY-MM-DD. Defaults to the import date if omitted |
| Date last visited | No | Date of the customer's most recent visit, in YYYY-MM-DD. Defaults to the import date if omitted |
| Notes | No | Free-text internal notes about the customer |
Transactions (By Group / Location)
Order-level records. Scoped to the company group you're migrating into.
| Field | Required | Description |
|---|---|---|
| First name | No | Customer's first name on the transaction |
| Last name | No | Customer's last name |
| Yes | Customer email used to link the transaction to a customer record | |
| Phone | No | Customer phone |
| Transaction date | No | When the transaction was created. Format YYYY-MM-DD HH:MM:SS or YYYY-MM-DD (omit time if not known) |
| Transaction status | No | One of active or cancelled |
| Order number | Yes | Your existing order/invoice number. Used as the unique identifier and to link bookings to this transaction |
| Price | No | Subtotal before taxes, fees, or discounts |
| Total | No | Total amount on the transaction |
| Paid | No | Amount already paid |
| Due | No | Outstanding balance |
| Taxes | No | Tax amount |
| Fees | No | Fee amount |
| Discount | No | Discount amount applied |
| Refunded | No | Amount refunded |
| Notes | No | Free-text internal notes |
Bookings (By Group / Location)
Booking slots and reservations. Migrate after Transactions so each booking can be linked to its transaction by Order number.
| Field | Required | Description |
|---|---|---|
| Booking name | Yes | Name of the event corresponding to this booking. Must match an existing event in Events > Event settings |
| Booking start date | Yes | Date the booking starts, in YYYY-MM-DD |
| Booking end date | No | Date the booking ends, in YYYY-MM-DD |
| Booking start time | Yes | Booking start time in HH:MM:SS |
| Booking end time | Yes | Booking end time in HH:MM:SS |
| Group size | No | Number of participants in the booking |
| Price | No | Price for the booking |
| Status | Yes | Slot status. One of available, booked, cancelled, blocked, call_to_book |
| Order number | No | The corresponding transaction's Order number, when applicable. Links the booking to a transaction in your data |
| Notes | No | Free-text booking notes |
| Reservation type | No | public for public bookings (multiple groups can book the same slot, no duplicate check applied) or private for private bookings |
Waivers (By Group / Location)
Signed waivers tied to a customer and an event. Migrate after Bookings so the corresponding events exist.
| Field | Required | Description |
|---|---|---|
| First name | No | Signer's first name |
| Last name | No | Signer's last name |
| Yes | Signer's email used to link to the customer record | |
| Birthday | No | Signer's date of birth in YYYY-MM-DD |
| Phone | No | Signer's phone |
| Date signed | No | When the waiver was signed. Format YYYY-MM-DD HH:MM:SS or YYYY-MM-DD (omit time if unknown) |
| Booking name | Yes | Name of the event the waiver is for. Must match an existing event in Events > Event settings |
| Booking start date | Yes | Booking date in YYYY-MM-DD |
| Booking end date | No | Booking end date in YYYY-MM-DD |
| Booking start time | Yes | Booking start time in HH:MM:SS |
| Booking end time | Yes | Booking end time in HH:MM:SS |
Gift cards
Gift card records.
| Field | Required | Description |
|---|---|---|
| Gift card code | Yes | The unique gift card code your customers redeem |
| Personal message | No | Personal message attached to the gift card |
| Date purchased | No | Purchase date in YYYY-MM-DD |
| Delivery date | No | Scheduled delivery date in YYYY-MM-DD |
| Expiration date | No | Expiration date in YYYY-MM-DD |
| Status | No | active or deactivated. Defaults to active when omitted |
| Total value | Yes | Total amount loaded on the gift card |
| Spent | Yes | Amount already spent (0 for unused cards) |
| Order number | No | Links the gift card as a purchase under an existing transaction with this Order number |
| Customer first name | No | Purchaser first name |
| Customer last name | No | Purchaser last name |
| Customer email | Yes | Purchaser email used to link to the customer record |
| Recipient email | Yes | Email of the gift card recipient |
| Recipient first name | No | Recipient first name |
| Recipient last name | No | Recipient last name |
Customer credit (Global)
Store-credit balances on customer accounts. Migrated once per account.
| Field | Required | Description |
|---|---|---|
| First name | No | Customer's first name |
| Last name | No | Customer's last name |
| Yes | Customer email used to link the credit to the customer record | |
| Total value | Yes | Total credit issued |
| Remaining | No | Remaining credit balance |
| Spent | No | Amount of credit already spent |
Promo codes (By Group / Location)
Promo and discount codes.
| Field | Required | Description |
|---|---|---|
| Promo name | Yes | The promo code itself (the string customers redeem) |
| Description | No | Internal description of the promo |
| Redemption type | Yes | value for fixed-amount discounts (e.g., $10 off) or percentage for percent-based discounts (e.g., 10% off) |
| Value amount | Yes | The numeric discount value (e.g., 10 for $10 off or 10%) |
| Quantity | No | Total redemptions allowed. Set to -1 for unlimited |
| Number of times used | No | Existing usage count to carry over |
| Applicable to gift cards and merchandise | No | Whether the promo can apply to gift cards and merchandise. Boolean: 1 / 0 or true / false |
| Promo code group | No | Optional. Links promo codes to an existing promo code template by name |
Reference
Date and time formats
| Field type | Format |
|---|---|
| Date-only fields (Birthday, Booking start date, Date purchased, etc.) | YYYY-MM-DD |
| Date-time fields (Date signed, Transaction date) | YYYY-MM-DD HH:MM:SS or YYYY-MM-DD if you want to omit the exact time |
| Time-only fields (Booking start time, Booking end time) | HH:MM:SS |
Boolean values
Boolean columns accept 1 / 0 or true / false.
Enum values
| Field | Allowed values |
|---|---|
| Status (Bookings) | available, booked, cancelled, blocked, call_to_book |
| Reservation type (Bookings) | public, private |
| Transaction status | active, cancelled |
| Redemption type (Promo codes) | value, percentage |
| Status (Gift cards) | active, deactivated |
| Country (Customers) | Two-letter ISO 3166-1 alpha-2 code |
Migration log row actions
| Action | Shown when |
|---|---|
| View records | Always |
| View duplicates | The run had duplicate records skipped |
| Download error records | The run had failed records |
| Purge records | The user has the Delete migrations permission |
Migration log statuses and counts
| Element | Description |
|---|---|
| Records imported | Successful row count over the total uploaded, with a progress bar during the import |
| Failed records | Count of rows that didn't import |
| Duplicates | Count of rows skipped because they already existed |
| Purged badge | Appears in place of the count when the run has been purged |
Empty state
If no migrations have been run yet, the migration log table shows the warning triangle icon and "No data has been uploaded yet."
Good to know
- Migrations are performed by Off The Couch administrators only. The upload control on the page is hidden for non-OTC accounts. Send your prepared files to nick@offthecouch.io or on Discord to schedule a run.
- Headers are case-sensitive. "Email" works; "email" or "EMAIL" doesn't. Use the copy icon on the page (or the schemas in this article) to grab the exact header text and avoid typos.
- The recommended file order in Migration Advice is the safe path: Customers, Transactions, Bookings, Waivers, Gift cards, Customer credit, Promo codes. Following the order means each migration has the data it needs to link records correctly (e.g., bookings link to transactions by Order number, waivers link to bookings by event name).
- Customers and Customer credit are global. The other migration types are scoped to the company group you're migrating into. Switch groups before sending the data if you have multiple locations.
- Bookings and Waivers require corresponding events in Events > Event settings. Create the events first; they're matched by exact name in the Booking name column.
- Country for the Customers schema must be a two-letter ISO 3166-1 alpha-2 code, not the full country name. Examples: US, CA, GB, AU, DE, NZ. The page has a Download reference link for the full code list.
- Order number in the Bookings schema is what links a booking to its transaction. Make sure the Transactions migration ran first and that the same Order number is used in the Bookings file.
- Public bookings (Reservation type =
public) are not checked for duplicates because the same slot can host multiple parties. - Promo codes: set Quantity to
-1to allow unlimited redemptions. Existing usage counts can be carried over via Number of times used. - Failed rows are downloadable as CSV via Download error records in the migration log's three-dot menu. Each failed row has its error reason in a column. Fix the issues and re-send only the corrected rows.
- Purging removes only records imported by that specific run. It doesn't affect records that already existed before the run.
FAQ
Q: Can I upload migrations myself?
A: No, not yet. The page restricts uploads to Off The Couch administrators. Prepare your files using the schemas in this article and send them to nick@offthecouch.io or contact Nick on Discord. He'll run the imports for you.
Q: I got the email wrong but the rest of my CSV is fine. What now?
A: Once a migration has run, the imported records can be purged via the Purge records action in the migration log. After purging, fix the email column and resend the corrected file.
Q: My file failed to upload entirely. What do I do?
A: Confirm the file is .csv (not .xlsx), the headers match exactly (case-sensitive), and the date and time formats match the Date and time formats reference. Re-export from your spreadsheet and resend.
Q: Some rows imported but others failed. Where are the failures?
A: In the Migration log tab, open the three-dot menu on the run and click Download error records. The downloaded CSV includes a column explaining why each row failed. Fix and resend only the failed rows.
Q: Can I undo a migration?
A: Yes. Use Purge records in the migration log's three-dot menu. This deletes every record imported by that specific run. Records that already existed before the run are untouched.
Q: Why do my booking imports fail with an "event not found" error?
A: The Booking name column must exactly match an event in your account. Create the events first under Events > Event settings, then resend the bookings file.
Q: Why do my waiver imports fail?
A: Waivers require both the customer (matched by Email) and the event (matched by Booking name) to exist. Run the Customers migration first, then create matching events, then send the waivers.
Q: Why is the Purge records option missing from a row's menu?
A: It requires the Delete migrations permission. Ask an admin to grant it in Settings > User management.
Q: What does "By Group / Location" mean in the migration order?
A: That migration type is scoped to a single company group. If you have multiple locations, you'll need a separate file per group, with the right group selected in the group picker before sending each one.
Q: My country names are full names like "United States". Will the migration accept them?
A: No. The Country column must be a two-letter ISO 3166-1 alpha-2 code (e.g., US). Convert before sending. The page has a Download reference link with the full country-to-code mapping.