# Field Specifications (FDIC Examination Ready)

## Header Record Field Specifications

The Header Record contains reporter identification and file metadata. One Header Record per file.

### Record Identifier

| Field | Length | Type | Value |
|  --- | --- | --- | --- |
| Record Identifier | 6 | Alpha | `HEADER` |


### Program Identifiers

| Field | Length | Source | Description |
|  --- | --- | --- | --- |
| Innovis Program Identifier | 10 | `loan_type.metro2_innovis_program_identifier` | Subscriber code for Innovis |
| Equifax Program Identifier | 10 | `loan_type.metro2_equifax_program_identifier` | Subscriber code for Equifax |
| Experian Program Identifier | 5 | `loan_type.metro2_experian_program_identifier` | Subscriber code for Experian |
| TransUnion Program Identifier | 10 | `loan_type.metro2_transunion_program_identifier` | Subscriber code for TransUnion |


**Logic:** Only the program identifier for the target bureau is populated; others are blank.

### Activity Date

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Activity Date | 8 | Date (MMDDYYYY) | `month_end_date` |


**Calculation:**


```
activity_date = report_date.replace(day=1) - 1 day
# Example: report_date = 2026-01-15 → activity_date = 2025-12-31
```

### Date Created

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Date Created | 8 | Date (MMDDYYYY) | `utc_now().date()` |


Description: The date the file was generated.

### Reporter Information

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Reporter Name | 40 | Alphanumeric | `loan_type.metro2_reporter_name` |
| Reporter Address | 96 | Alphanumeric | `loan_type.metro2_reporter_address` |
| Reporter Telephone | 10 | Numeric | `loan_type.metro2_reporter_telephone_number` |


**Reporter Telephone Formatting:**

- Must be US phone number
- Stored without country code
- Format: `5551234567` (10 digits)


### Software Information

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Software Vendor Name | 40 | Alphanumeric | `loan_type.metro2_software_vendor_name` |
| Software Version | 5 | Alphanumeric | `loan_type.metro2_software_version_number` |
| MicroBilt/PRBC Identifier | 10 | Alphanumeric | `loan_type.metro2_micro_bilt_prbc_program_identifier` |


## Base Segment Field Specifications

The Base Segment contains the core account information. One per tradeline.

### Record Descriptor Word

| Field | Length | Type | Description |
|  --- | --- | --- | --- |
| Record Descriptor Word | 4 | Numeric | Total record length in bytes (including this field) |


**Calculation:**


```
rdw = len(record_string.encode()) + 4
# Formatted as 4-digit zero-padded number
```

### Processing Indicator

| Field | Length | Type | Value |
|  --- | --- | --- | --- |
| Processing Indicator | 1 | Numeric | `1` (standard processing) |


### Time Stamp

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Time Stamp | 14 | DateTime | `utc_now().isoformat()` |


Format: `MMDDYYYYHHMMSS`

### Identification Number

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 3 | 20 | Alphanumeric | Bureau-specific identifier from loan type |


**Logic:**


```
field_name = {
  'Innovis': 'metro2_innovis_identification_number',
  'Equifax': 'metro2_equifax_identification_number',
  'Experian': 'metro2_experian_identification_number',
  'TransUnion': 'metro2_transunion_identification_number'
}[reporting_agency]

identification_number = loan_type[field_name]

# For migrated loans with account number change:
IF account_info_change_indicator.is_change_identification_number:
  identification_number = old_identification_number  # Report old in base
  # New number goes in L1 segment
```

### Consumer Account Number

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 5 | 30 | Alphanumeric | `loan.external_id` or `loan.public_id` |


**Logic:**


```
IF credit_agency.use_loan_external_id AND loan.external_id:
  consumer_account_number = loan.external_id
ELSE:
  consumer_account_number = loan.public_id

# For migrated loans with account number change:
IF account_info_change_indicator.is_change_consumer_account_number:
  consumer_account_number = old_consumer_account_number  # Report old in base
  # New number goes in L1 segment
```

### Portfolio Type

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 6 | 1 | Alpha | `loan_type.metro2_portfolio_type` |


**Values:**

| Code | Description |
|  --- | --- |
| `C` | Line of Credit |
| `I` | Installment |
| `M` | Mortgage |
| `O` | Open |
| `R` | Revolving |


### Account Type

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 7 | 2 | Alphanumeric | `loan_type.metro2_account_type` |


**Formatting:** Zero-padded (e.g., `01`, `48`)

### Date Opened

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 8 | 8 | Date | `loan.activated_at` |


**Format:** `MMDDYYYY`

### Credit Limit

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 9 | 9 | Monetary | Calculated |


**Logic:**


```
IF portfolio_type IN ('I', 'M'):  # Installment/Mortgage
  credit_limit = 0
ELSE:  # Open-ended
  credit_limit = loan.get_credit_limit(effective_at=snapshot_date)

RETURN round_based_on_loan_type(credit_limit)
```

### Highest Credit Amount / Original Loan Amount

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 10 | 9 | Monetary | Calculated |


**Logic:**


```
IF portfolio_type IN ('I', 'M'):  # Installment/Mortgage
  highest_credit = loan.get_amount_financed_at_origination()
ELSE:  # Open-ended
  # Maximum principal balance during the reporting period
  highest_credit = max(snapshot.outstanding_balance_principal_amount for snapshot in period_snapshots)

RETURN round_based_on_loan_type(highest_credit)
```

### Terms Duration

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 11 | 3 | Alphanumeric | Calculated |


**Logic:**


```
IF portfolio_type IN ('I', 'M'):  # Installment/Mortgage
  # Convert to months
  periods_per_month = AUTOPAY_TO_DUE_DATES[Monthly][loan.payment_frequency_at_origination]
  terms_duration = loan.duration_at_origination / periods_per_month
  RETURN format_as_integer(terms_duration)  # e.g., "036" for 36 months
ELSE:
  RETURN {
    'R': 'REV',  # Revolving
    'C': 'LOC',  # Line of Credit
    'O': '001'   # Open
  }[portfolio_type]
```

### Terms Frequency

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 12 | 1 | Alpha | `loan.current_periodic_payment_loan_schedule().frequency` |


**Mapping:**

| Peach Frequency | Metro 2 Code |
|  --- | --- |
| Monthly | `M` |
| TwiceMonthly | `E` |
| EveryTwoWeeks | `B` |
| Weekly | `W` |


### Scheduled Monthly Payment Amount

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 13 | 9 | Monetary | Calculated |


**Logic:** See [Calculations](/credit-reporting/calculations) section.

### Actual Payment Amount

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 17 | 9 | Monetary | Calculated |


**Logic:** See [Calculations](/credit-reporting/calculations) section.

### Account Status

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 17A | 2 | Alphanumeric | Calculated |


**Logic:** See [Account Status Codes](/credit-reporting/account-status) section.

### Payment Rating

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 17B | 1 | Alpha | Calculated |


**Logic:** See [Payment Rating and History](/credit-reporting/payment-rating-history) section.

### Payment History Profile

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 18 | 24 | Alphanumeric | Calculated |


**Logic:** See [Payment Rating and History](/credit-reporting/payment-rating-history) section.

### Special Comment

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 19 | 2 | Alphanumeric | Calculated |


**Logic:** See [Special Circumstances](/credit-reporting/special-circumstances) section.

### Compliance Condition Code

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 20 | 2 | Alphanumeric | Calculated |


**Logic:** See [Special Circumstances](/credit-reporting/special-circumstances) section.

### Current Balance

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 21 | 9 | Monetary | `snapshot.outstanding_balance_total_amount` |


**Logic:** See [Calculations](/credit-reporting/calculations) section.

### Amount Past Due

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 22 | 9 | Monetary | Calculated |


**Logic:** See [Calculations](/credit-reporting/calculations) section.

### Original Charge-off Amount

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 23 | 9 | Monetary | `LoanChargeOffProcessedEvent.amount` |


**Logic:**


```
IF snapshot.loan_status != 'ChargedOff' AND lender_status != '97':
  original_charge_off_amount = NULL  # Field left blank
ELSE:
  event = find_most_recent_charge_off_event(loan_id)
  IF event AND event.amount > 0:
    original_charge_off_amount = event.amount
  ELSE:
    # Fallback to current charged-off balance
    original_charge_off_amount = loan.balances.chargedOffBalances.chargedOffTotalAmount
```

### Date of Account Information

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 24 | 8 | Date | Calculated |


**Logic:**


```
IF loan.status IN ('Active', 'Accelerated', 'Frozen', 'ChargedOff'):
  date_of_account_info = month_end_date
ELIF loan.status = 'PaidOff':
  date_of_account_info = min(loan.paid_off_at.date(), month_end_date)
ELIF loan.status = 'Canceled':
  date_of_account_info = min(loan.canceled_at.date(), month_end_date)
```

### FCRA Date of First Delinquency

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 25 | 8 | Date | Calculated |


**Logic:**


```
FUNCTION calculate_fcra_dofd(loan, snapshot, account_status, payment_rating, 
                              consumer_info_indicator, reported_month):
  
  # For voluntary surrender
  IF lender_supplied_status = '95':
    IF loan.canceled_at:
      RETURN loan.canceled_at.date()
    RETURN reporting_status.reporting_end_date
  
  # For delinquent accounts
  IF account_status.is_non_current:
    due_date = get_latest_overdue_due_date()
    IF due_date:
      RETURN due_date + 30 days
  
  # For paid-off accounts that were delinquent
  IF account_status = '13' AND payment_rating.is_non_current:
    due_date = get_latest_overdue_due_date()
    IF due_date:
      RETURN due_date + 30 days
  
  # For bankruptcy with current status
  # CRITICAL: Only set DOFD from bankruptcy when specific conditions are met
  IF consumer_info_indicator IN ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '1A', 'V'):
    IF account_status = '11' OR (account_status = '13' AND payment_rating = '0'):
      # Find qualifying bankruptcy case
      # CRITICAL: Loan must be associated with the case
      bk_case = find_case(
        type = 'bankruptcy',
        loan_associated = True,  # Loan must be linked to this case
        status IN ('Processing', 'Completed'),
        date_filter: (
          (reported_month >= case.courtCaseFiledDate::month/year 
           AND reported_month <= coalesce(case.courtCaseClosedDate::month/year,
                                           case.courtCaseDebtorDispositionDate::month/year))
          OR
          (reported_month >= case.courtCaseFiledDate::month/year 
           AND case.courtCaseClosedDate == empty 
           AND case.courtCaseDebtorDispositionDate == empty)
        )
      )
      
      # If multiple cases exist, select the most recent based on case.createdAt
      
      IF bk_case IS NOT NULL AND bk_case.courtCaseFiledDate IS NOT empty:
        RETURN bk_case.courtCaseFiledDate
  
  RETURN NULL  # No DOFD applicable
```

### Date Closed

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 26 | 8 | Date | Calculated |


**Logic:**


```
IF snapshot.loan_status = 'PaidOff' OR lender_status IN ('61', '63'):
  RETURN loan_paid_off_event.effective_at.date()

IF snapshot.loan_status = 'ChargedOff':
  IF has_outstanding_balance:
    IF is_installment:
      RETURN NULL
    ELIF loan.is_closed:  # LOC closed
      RETURN loan_closed_event.effective_at.date()
    ELSE:  # LOC open
      RETURN NULL
  ELSE:  # No balance
    RETURN date_outstanding_balance_went_zero

IF is_open_ended AND loan.is_closed:
  RETURN loan_closed_event.effective_at.date()

RETURN NULL
```

### Date of Last Payment

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 27 | 8 | Date | Calculated |


**Logic:** See [Calculations](/credit-reporting/calculations) section.

### Interest Type Indicator

| Field # | Length | Type | Source |
|  --- | --- | --- | --- |
| Field 28 | 1 | Alpha | `loan_type.metro2_interest_type_indicator` |


**Values:**

| Code | Description |
|  --- | --- |
| `F` | Fixed |
| `V` | Variable |
| (blank) | Not applicable |


## Consumer Information Field Specifications

Consumer demographic information is included in the Base Segment.

### Surname

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Surname | 25 | Alpha | `main_borrower.last_name` |


**Business Borrowers:** Left blank if borrower type is Business.

### First Name

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| First Name | 20 | Alpha | `main_borrower.first_name` |


### Middle Name

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Middle Name | 20 | Alpha | `main_borrower.middle_name` |


### Generation Code

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Generation Code | 1 | Alpha | `main_borrower.prefix` |


**Mapping:**

| Prefix | Code |
|  --- | --- |
| JR | `J` |
| SR | `S` |
| II | `2` |
| III | `3` |
| IV | `4` |
| V | `5` |
| VI | `6` |
| VII | `7` |
| VIII | `8` |
| IX | `9` |


### Social Security Number

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| SSN | 9 | Numeric | PersonIdentity (SSN type, primary) |


**Security:** SSNs are tokenized in storage; actual values retrieved from secure tokenizer at file generation time.

### Date of Birth

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Date of Birth | 8 | Date | `main_borrower.date_of_birth` |


**Format:** `MMDDYYYY`

### Telephone Number

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Telephone | 10 | Numeric | Consumer's phone from Contact records |


**Selection Logic:**

1. Primary Personal phone (Self affiliation)
2. Secondary Personal phone (Self affiliation)
3. Primary Home phone (Self affiliation)
4. Secondary Home phone (Self affiliation)


Must be US phone number; non-US numbers excluded.

### ECOA Code

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| ECOA Code | 1 | Alpha | Calculated |


**Values:**

| Code | Description |
|  --- | --- |
| `1` | Individual |
| `2` | Joint Contractual Liability |
| `3` | Authorized User |
| `5` | Co-maker or Guarantor |
| `7` | Maker |
| `T` | Terminated |
| `X` | Deceased |
| `W` | Business/Commercial |
| `Z` | Delete Consumer |


**Logic:**


```
# Override check
override = get_one_time_field_value(EcoaCode)
IF override:
  RETURN override

# Deceased case
IF deceased_case_exists(status='Completed', outcome='Approved'):
  RETURN 'X'

# Delete due to data corruption
IF is_deleted AND deleted_reason = 'LoanDataCorrupted':
  RETURN 'Z'

# Business borrower
IF main_borrower.borrower_type = 'Business':
  RETURN 'W'

# Default
RETURN '1'  # Individual
```

### Consumer Information Indicator

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Consumer Info Indicator | 2 | Alpha | Calculated |


**Logic:** See [Special Circumstances](/credit-reporting/special-circumstances) section.

### Address Fields

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Country Code | 2 | Alpha | ISO to Metro 2 mapping |
| First Line of Address | 32 | Alpha | `address.address_line1` |
| Second Line of Address | 32 | Alpha | `address.address_line2` |
| City | 20 | Alpha | `address.city` |
| State | 2 | Alpha | `address.state` |
| Postal Code | 9 | Alpha | `address.zip_code` |
| Address Indicator | 1 | Alpha | `C` (Verified) or `Y` (Known) |


**Address Selection:** Primary address with status `Primary` is used. Only reported if address has `Primary` status.

**Address Indicator Values:**

| Code | Description |
|  --- | --- |
| `C` | Verified |
| `Y` | Known to be address of primary consumer |


**Address Indicator Determination:**


```
FUNCTION determine_address_indicator(address):
  IF address.verified = True:
    RETURN 'C'  # Address has been verified (e.g., through postal validation)
  ELSE:
    RETURN 'Y'  # Known to be address of primary consumer but not verified
```

**Country Code Mapping:** Peach uses ISO 3166-1 alpha-2 codes internally, which are mapped to Metro 2 country codes. Example:

- `US` → `US`
- `CA` → `CN` (Canada)
- `GB` → `UK`


## Supplemental Segment Specifications

### K1 Segment (Original Creditor)

The K1 Segment reports the original creditor when different from the current reporter.

**Inclusion Criteria:**

- Not in `metro2_skip_segments` for the loan type
- `loan.originating_creditor_name_at_origination` is populated


**Fields:**

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Segment Identifier | 2 | Alpha | `K1` |
| Original Creditor Name | 30 | Alpha | `loan.originating_creditor_name_at_origination` |
| Creditor Classification | 2 | Numeric | `loan_type.metro2_creditor_classification` |


### K2 Segment (Purchased From / Sold To)

The K2 Segment reports ownership changes or collection agency assignments.

**Inclusion Criteria:**

- Not in `metro2_skip_segments`
- One of the following occurred in the reporting month:
  - Loan ownership changed with `reportK2Segment: true`
  - Collection agency assignment with `report_k2_segment: true`
  - Migration with `metro2_k2_purchased_from_name` set


**Fields:**

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Segment Identifier | 2 | Alpha | `K2` |
| Purchased From/Sold To Indicator | 1 | Numeric | `1` (Purchased From) or `2` (Sold To) |
| Name | 30 | Alpha | New owner/collector legal name |


**Purchased From/Sold To Logic:**


```
IF migration_purchased_from_name (first report of migrated loan):
  indicator = '1'  # Purchased From
  name = migration_purchased_from_name
ELSE:
  indicator = '2'  # Sold To
  name = new_investor_legal_name OR collection_agency_legal_name
```

### L1 Segment (Account Number Change)

The L1 Segment reports account number changes, typically during migration.

**Inclusion Criteria:**

- Not in `metro2_skip_segments`
- `account_info_change_indicator` is not empty
- First report of a migrated loan with account number change


**Fields:**

| Field | Length | Type | Source |
|  --- | --- | --- | --- |
| Segment Identifier | 2 | Alpha | `L1` |
| Account Info Change Indicator | 1 | Numeric | See below |
| New Consumer Account Number | 30 | Alpha | `loan.public_id` or `loan.external_id` |
| New Identification Number | 20 | Alpha | Bureau-specific identifier |


**Account Info Change Indicator Values:**

| Code | Description |
|  --- | --- |
| `1` | Consumer Account Number changed only |
| `2` | Identification Number changed only |
| `3` | Both Consumer Account Number and Identification Number changed |


## Trailer Record Specifications

The Trailer Record contains control totals for file validation.

### Record Structure

| Field | Length | Type | Description |
|  --- | --- | --- | --- |
| Record Identifier | 7 | Alpha | `TRAILER` |
| Total Base Records | 9 | Numeric | Count of Base Segments |
| Reserved | 9 | Numeric | Zeros |
| Status DF Count | 9 | Numeric | Count of status DF records |
| Reserved | 27 | Numeric | Zeros |
| Status DA Count | 9 | Numeric | Count of status DA records |
| Status 05 Count | 9 | Numeric | Count of status 05 records |
| Status 11 Count | 9 | Numeric | Count of status 11 records |
| Status 13 Count | 9 | Numeric | Count of status 13 records |
| ... | ... | ... | (One field per status code) |
| ECOA Z Count | 9 | Numeric | Count of ECOA code Z records |
| K1 Segment Count | 9 | Numeric | Count of K1 segments |
| K2 Segment Count | 9 | Numeric | Count of K2 segments |
| SSN Count | 9 | Numeric | Count of records with SSN |
| SSN Count (duplicate) | 9 | Numeric | Same as above |
| DOB Count | 9 | Numeric | Count of records with DOB |
| DOB Count (duplicate x3) | 27 | Numeric | Same as above (3 times) |
| Phone Count | 9 | Numeric | Count of records with phone |