# File requirements and parsing rules

Vintage accepts your loan data the way your core system exports it, with no required template. The few rules a file must meet to be read are stated below, and then exactly how each cell is read: when a value becomes a number, when it becomes a date, and when it is treated as no value at all.

The short version: upload **CSV or Excel `.xlsx`**, keep every file in one file group to the **same columns**, and know that Vintage never turns a blank or an unreadable cell into a zero.

## Accepted file types

**CSV and Excel `.xlsx` are the only accepted file types.** Anything else is refused when you choose it, with a message naming the two supported types.

- **Older Excel `.xls` files are refused**, including an older workbook renamed to `.xlsx` or `.csv`. The message tells you to save the workbook as `.xlsx` or export it as CSV.
- Other file types, such as `.txt`, `.pdf`, or `.zip`, are refused with the list of supported types.
- **An empty file** is refused where you choose it, and so is a file with column headings but no data rows under them.
- A file that cannot be read at all, for a reason Vintage does not recognize, is named: you are told that file, and any picked after it, were not added and what to check.

CSV is the recommended format, especially for a large portfolio. A CSV file has **no size limit of its own**. A single file is bounded only by what Vintage can address internally, about 2 GB, and a file that would cross that is refused while it is being added, with a message saying so, rather than after it has been sent.

An Excel workbook is read entirely in your browser, so Excel carries caps that CSV does not:

| Cap | Limit |
|---|---|
| One Excel file | 50 MB |
| Excel files in one upload | 5 |
| Excel across one upload | 100 MB |

Crossing any cap is refused with guidance to export to CSV or split the work into separate uploads. When you add an Excel file, Vintage also shows a dismissible note: for a large portfolio, export to CSV instead, which is faster and lighter on your browser.

**A workbook with more than one sheet is read from its first sheet only.** Vintage names that sheet beside the file where it is staged, and again on the Review screen, with how many sheets were left out. To upload a different sheet, export it on its own and upload that.

**Vintage reads each cell's stored value, not the text the spreadsheet displays.** A date stored as a spreadsheet date becomes that calendar date, and a stored number becomes that number, so how a cell is formatted never changes what Vintage reads.

## One column layout per file group

Every upload sorts files into up to three **file groups**: Snapshot files, Origination files, and Transaction files. You declare each file's group by choosing it; Vintage never guesses. Within one upload, **every file in a group must have the same column names**, in any order. A file whose columns do not match its group's is staged with the mismatch named, and **Continue** stays blocked until you remove or fix it.

Across different uploads, a group's columns may change. A renamed column reads as one column removed and another added. See [Choosing files](/uploading/choosing-files/).

## Column headings

Each column needs a distinct, non-blank heading, because Vintage records every decision about a column by its name. A file with a duplicated or blank heading cannot pass Choose Files or be confirmed at the Remove PII step. If processing finds such a file, it is refused with a sentence that names the problem and the fix, and the only action offered is **Remove**, because retrying would fail the same way.

## Rows whose cell count does not match the heading row

In a CSV, a value containing a comma that is not quoted (`Smith, John`) splits into two cells and pushes every later value one column to the right. The row's values can no longer be trusted to belong to their columns, including the identifier columns protected in your browser. So a row with more or fewer cells than the heading row is **skipped entirely**: never trimmed, padded, or partly kept.

Vintage counts skipped rows and states the count on the Column Mapping screen and again on the Review screen, with the fix: correct the export and upload again. Every row figure Vintage shows counts only imported rows. Spreadsheet files address cells by position, so their values cannot shift columns.

## How a value becomes a number

Every cell is read by the type of the field it is mapped to: numeric, date, categorical, or text. One grammar reads every numeric column the same way.

- **Blank is no value, never zero.** An empty cell, a cell of spaces, and the placeholders `NA`, `N/A`, `N-A`, `NULL`, `NONE`, `EMPTY`, and a lone `-`, in any capitalization, mean "no value". So does a currency symbol with no digits, such as `$ -`.
- **Spaces** at the start or end are ignored.
- **Thousands commas** are removed only when they group digits correctly in threes (`1,234,567`). A malformed grouping (`1,5`, `12,34`, `1,2345`, or a zero-led `0,125`) is never reinterpreted as a different number. The decimal point is always a period, so European-style `1.234,56` is not read as a number.
- **Currency** markers are removed: a leading `$`, euro, pound, or yen symbol, and a leading or trailing three-letter code (`USD`, `EUR`, `GBP`, `JPY`, `CAD`, `AUD`, `CHF`, `CNY`).
- **Negatives** read the same whichever way they are written: a minus sign and a currency symbol combine in either order, and accounting parentheses mean negative whether the symbol sits inside or outside them.
- **A trailing percent sign is removed** and the number is kept as written: `5%` reads as 5. What that numeral means is decided by the field's percent format for the whole column, never cell by cell. See [Percent columns](#percent-columns).
- **Not read as numbers:** abbreviated magnitudes (`125k`, `1.2M`), scientific notation (`1e3`), units other than a recognized currency (`10 bps`), and extra punctuation (`1.2.3`).
- **Precision.** Numbers are stored to six decimal places; a value needing more is rounded.
- **Very large numbers.** A numeric field value with 15 or more digits before the decimal point is treated as no value, counted, and shown to you. It is never overflowed or rounded into a different number. A long identifier mapped as text or as a Custom attribute (a column kept under its own name) keeps every digit exactly as uploaded.

A value that is not a number under these rules is kept, but it counts as no value for a numeric field. One such cell never disqualifies the column: a column's type is inferred from the cells that carry data, so a numeric column with the occasional `Pending` stays numeric, and a sparse amount column that is blank on most rows still reads as numeric.

Cells Vintage could not read cleanly, and numbers too large to store, are counted per file and shown as a notice on the Column Mapping screen. Those rows are still imported; you decide whether to fix the export or continue.

### Worked examples: numbers

| In your file | Read as | Why |
|---|---|---|
| `1234` | 1234 | A plain number. |
| `1,234.56` | 1234.56 | Correctly grouped thousands commas are removed. |
| `1,5` | no value | Commas that do not group digits in threes are never read as 15. |
| `0,125` | no value | A zero before the comma is not thousands grouping; never read as 125. |
| `1.234,56` | no value | European-style decimals are not read. |
| `$1,234.56` | 1234.56 | A leading currency symbol is removed. |
| `1234.56 USD` | 1234.56 | A trailing currency code is removed. |
| `-$1,234.56` | -1234.56 | Sign before the symbol. |
| `$-1,234.56` | -1234.56 | Sign after the symbol reads the same. |
| `(500)` | -500 | Parentheses mean negative. |
| `($1,200)` | -1200 | Symbol inside the parentheses. |
| `$ (1,234.00)` | -1234 | Symbol outside the parentheses, with a space. |
| `5%` | 5 | The percent sign is removed; the number is kept as written. |
| `(5%)` | -5 | A negative percent. |
| `+7` | 7 | A leading plus sign is allowed. |
| `.5` | 0.5 | A leading decimal point is allowed. |
| (blank) | no value | Blank is empty, never 0. |
| `NA` | no value | A missing-data placeholder. |
| `-` | no value | A lone dash is missing, never 0 or negative. |
| `$ -` | no value | An accounting-style dash is missing, never 0. |
| `Pending` | no value | Text is not a number. |
| `125k` | no value | Abbreviated magnitudes are not read. |
| `$1.2M` | no value | Abbreviated magnitudes are not read. |
| `10 bps` | no value | Unrecognized units are not read. |
| `1e3` | no value | Scientific notation is not read. |
| `5.` | no value | A trailing decimal point with no digits. |

"No value" means the cell is kept in your data but treated as empty for a numeric field. It never overwrites a value Vintage already knows for that loan.

## Percent columns

A numeric field also carries a **format**: number, currency, or one of two percent formats. Number and currency change only how values are displayed. A percent format is the reading instruction for the **whole column**:

| Format | Your file writes 8.25% as | `5%` in this column means |
|---|---|---|
| **Percent points** | `8.25` | 5% |
| **Decimal fraction** | `0.0825` | 500% (the column's format governs, not the cell's decoration) |

Every percent value is stored as percent points and displayed as `X%`, whichever format the file used. Vintage suggests a format, marked **matches your data**; when every value in a column carries a percent sign, the suggestion is Percent points. Interest Rate defaults to Percent points. Changing a percent format later re-reads the column from the original uploaded values and is confirmed before it takes effect. See [Declaring formats](/uploading/declaring-formats/).

## How a value becomes a date

A date column's **format is declared once for the column** and then reads every cell the same way. Vintage suggests it from the column's own values, shows it on the Column Mapping screen, and lets you correct it.

- **Always recognized, whatever the declared order:** year-first dates (`2024-01-15`, `2024/1/15`, `20240115`), month codes (`2024-01`, `202401`), and dates with a written month name (`Jan-2024`, `January 2024`, `15-Jan-2024`, `Jan 15, 2024`), abbreviated or in full, in any capitalization. A month-level value is read as that month, with the day pinned to the first.
- **Two numbers and a year** (`04/07/2025`, `01-15-2024`) could be month first or day first, so they are read in the column's declared order. Your data settles it when it can: a value whose first number is over 12 proves day first, and one whose second number is over 12 proves month first. When nothing in the column settles it, Vintage **defaults to US month/day order** and shows that as a correctable suggestion. An ambiguous date order never blocks finalizing.
- **Only real calendar dates** are read. February 30 and April 31 are not; February 29 is read only in a leap year.
- **A bare year** such as `2024` is not read as a date.
- A cell that does not fit the column's declared format is kept but treated as no value for that field, and counted as **wrong format** so Vintage can name the field to fix. It is never flipped into the other order.

### Worked examples: dates

| In your file | Declared order | Read as | Why |
|---|---|---|---|
| `2024-01-15` | Year first | 2024-01-15 | Year-first dates are always recognized. |
| `20240115` | Year first | 2024-01-15 | An eight-digit date code. |
| `2024-01` | Year first | 2024-01 | A month code; the day is pinned to the first. |
| `04/07/2025` | Month/Day | 2025-04-07 | April 7. |
| `04/07/2025` | Day/Month | 2025-07-04 | The same text is July 4 under day-first order. |
| `31/12/2024` | Month/Day | no value | Does not fit the declared order; never flipped. |
| `Jan-2024` | Month name | 2024-01 | A written month name is never ambiguous. |
| `15-Jan-2024` | Month/Day | 2024-01-15 | Month-name dates read under any declared order. |
| `02/29/2024` | Month/Day | 2024-02-29 | 2024 is a leap year. |
| `02/29/2023` | Month/Day | no value | 2023 is not a leap year. |
| `2024` | Month/Day | no value | A bare year is never a date. |

**Correct a date order before you finalize:** The declared date order is applied as each file is read in. Correcting it **after** an upload is finalized does not re-read dates already stored; it takes uploading the file again. Check the date format on the Column Mapping screen before you continue.

Snapshot months are matched by the calendar date they parse to, not by their text, so an export whose format drifts (`2024-01` one month, `Jan-2024` the next) never creates a duplicate month. See [Uploading month after month](/uploading/uploading-month-after-month/).

## Loan ID and Tax ID values

Identifier columns follow two extra rules before they are scrambled in your browser. Both are stated in full in [Removing personal information](/uploading/removing-personal-information/).

- **Loan IDs are normalized** so cosmetic differences do not split one loan's history: whitespace is removed, letters match regardless of case, and leading zeros are dropped, so `0012345`, `12345`, and `12 345` are the same loan. Separators such as `-` and `/`, and every other digit, stay significant. Tax IDs also drop every non-alphanumeric character.
- **A placeholder is not an ID.** The blank-like placeholders above, and an all-zeros value such as `0`, `000000`, or `000-00-0000`, give the row no protected ID (the scrambled stand-in for a Loan ID) at all. A row with no usable Loan ID is kept as source data but cannot be linked to a loan; Review reports it as without a Loan ID.

## The public parsing-rules page

Vintage also publishes these numeric and date rules, with worked examples, at [app.vintagemodeling.com/parsing-rules](https://app.vintagemodeling.com/parsing-rules), a page you can open without signing in and share with whoever prepares your exports.

## Related

- [Choosing files](/uploading/choosing-files/)
- [Declaring formats](/uploading/declaring-formats/)
- [Mapping columns](/uploading/mapping-columns/)
- [Preparing your data](/getting-started/preparing-your-data/)
- [Limits](/reference/limits/)