The Header Record contains reporter identification and file metadata. One Header Record per file.
| Field | Length | Type | Value |
|---|---|---|---|
| Record Identifier | 6 | Alpha | HEADER |
| 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.
| 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| Field | Length | Type | Source |
|---|---|---|---|
| Date Created | 8 | Date (MMDDYYYY) | utc_now().date() |
Description: The date the file was generated.
| 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)
| 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 |
The Base Segment contains the core account information. One per tradeline.
| 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| Field | Length | Type | Value |
|---|---|---|---|
| Processing Indicator | 1 | Numeric | 1 (standard processing) |
| Field | Length | Type | Source |
|---|---|---|---|
| Time Stamp | 14 | DateTime | utc_now().isoformat() |
Format: MMDDYYYYHHMMSS
| 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| 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| 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 |
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 7 | 2 | Alphanumeric | loan_type.metro2_account_type |
Formatting: Zero-padded (e.g., 01, 48)
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 8 | 8 | Date | loan.activated_at |
Format: MMDDYYYY
| 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)| 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)| 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]| 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 |
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 13 | 9 | Monetary | Calculated |
Logic: See Calculations section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 17 | 9 | Monetary | Calculated |
Logic: See Calculations section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 17A | 2 | Alphanumeric | Calculated |
Logic: See Account Status Codes section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 17B | 1 | Alpha | Calculated |
Logic: See Payment Rating and History section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 18 | 24 | Alphanumeric | Calculated |
Logic: See Payment Rating and History section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 19 | 2 | Alphanumeric | Calculated |
Logic: See Special Circumstances section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 20 | 2 | Alphanumeric | Calculated |
Logic: See Special Circumstances section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 21 | 9 | Monetary | snapshot.outstanding_balance_total_amount |
Logic: See Calculations section.
| Field # | Length | Type | Source |
|---|---|---|---|
| Field 22 | 9 | Monetary | Calculated |
Logic: See Calculations section.
| 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| 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)| 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| 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| Field # | Length | Type | Source |
|---|---|---|---|
| Field 27 | 8 | Date | Calculated |
Logic: See Calculations section.
| 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 demographic information is included in the Base Segment.
| Field | Length | Type | Source |
|---|---|---|---|
| Surname | 25 | Alpha | main_borrower.last_name |
Business Borrowers: Left blank if borrower type is Business.
| Field | Length | Type | Source |
|---|---|---|---|
| First Name | 20 | Alpha | main_borrower.first_name |
| Field | Length | Type | Source |
|---|---|---|---|
| Middle Name | 20 | Alpha | main_borrower.middle_name |
| 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 |
| 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.
| Field | Length | Type | Source |
|---|---|---|---|
| Date of Birth | 8 | Date | main_borrower.date_of_birth |
Format: MMDDYYYY
| Field | Length | Type | Source |
|---|---|---|---|
| Telephone | 10 | Numeric | Consumer's phone from Contact records |
Selection Logic:
- Primary Personal phone (Self affiliation)
- Secondary Personal phone (Self affiliation)
- Primary Home phone (Self affiliation)
- Secondary Home phone (Self affiliation)
Must be US phone number; non-US numbers excluded.
| 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| Field | Length | Type | Source |
|---|---|---|---|
| Consumer Info Indicator | 2 | Alpha | Calculated |
Logic: See Special Circumstances section.
| 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 verifiedCountry Code Mapping: Peach uses ISO 3166-1 alpha-2 codes internally, which are mapped to Metro 2 country codes. Example:
US→USCA→CN(Canada)GB→UK
The K1 Segment reports the original creditor when different from the current reporter.
Inclusion Criteria:
- Not in
metro2_skip_segmentsfor the loan type loan.originating_creditor_name_at_originationis 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 |
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_nameset
- Loan ownership changed with
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_nameThe L1 Segment reports account number changes, typically during migration.
Inclusion Criteria:
- Not in
metro2_skip_segments account_info_change_indicatoris 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 |
The Trailer Record contains control totals for file validation.
| 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 |