import {z} from 'zod' export type APIClientID = string; export type APISecret = string; export type AssetReportFreddieGetRequest = {audit_copy_token: string, client_id?: APIClientID | undefined, secret?: APISecret | undefined} | {[key: string]: unknown}; export type CreateApiCreditAssetReportFreddieMacGetArgs = {body: AssetReportFreddieGetRequest}; export type CreateApiCreditAssetReportFreddieMacGetOptions = {baseUrl?: string | undefined, clientId?: string | undefined, secret?: string | undefined, plaidVersion?: string | undefined, fetch?: typeof fetch | undefined}; export const loanIdentifierType = z.enum(["LenderLoan", "UniversalLoan"]).nullable(); export const loanIdentifier = z.object({LoanIdentifier: z.string().nullable(), LoanIdentifierType: loanIdentifierType}).and(z.record(z.string(), z.unknown())); export const loanIdentifiers = z.object({LOAN_IDENTIFIER: loanIdentifier}).and(z.record(z.string(), z.unknown())); export const loan = z.object({LOAN_IDENTIFIERS: loanIdentifiers}).and(z.record(z.string(), z.unknown())); export const loans = z.object({LOAN: loan}).and(z.record(z.string(), z.unknown())); export const individualName = z.object({FirstName: z.string(), LastName: z.string()}).and(z.record(z.string(), z.unknown())); export const partyIndividual = z.object({NAME: individualName}).and(z.record(z.string(), z.unknown())); export const partyRoleType = z.literal("Borrower"); export const roleDetail = z.object({PartyRoleType: partyRoleType}).and(z.record(z.string(), z.unknown())); export const role = z.object({ROLE_DETAIL: roleDetail}).and(z.record(z.string(), z.unknown())); export const roles = z.object({ROLE: role}).and(z.record(z.string(), z.unknown())); export const taxpayerIdentifierType = z.enum(["IndividualTaxpayerIdentificationNumber", "SocialSecurityNumber"]).nullable(); export const taxpayerIdentifier = z.object({TaxpayerIdentifierType: taxpayerIdentifierType, TaxpayerIdentifierValue: z.string().nullable()}).and(z.record(z.string(), z.unknown())); export const taxpayerIdentifiers = z.object({TAXPAYER_IDENTIFIER: taxpayerIdentifier}).and(z.record(z.string(), z.unknown())); export const party = z.object({INDIVIDUAL: partyIndividual, ROLES: roles, TAXPAYER_IDENTIFIERS: taxpayerIdentifiers}).and(z.record(z.string(), z.unknown())); export const parties = z.object({PARTY: z.array(party)}).and(z.record(z.string(), z.unknown())); export const reportingInformation = z.object({ReportingInformationIdentifier: z.string()}).and(z.record(z.string(), z.unknown())); export const serviceProductFulfillmentIdentifier = z.enum(["VOA", "VOE"]); export const serviceProductFulfillmentDetail = z.object({VendorOrderIdentifier: z.string().nullable(), ServiceProductFulfillmentIdentifier: serviceProductFulfillmentIdentifier}).and(z.record(z.string(), z.unknown())); export const serviceProductFulfillment = z.object({SERVICE_PRODUCT_FULFILLMENT_DETAIL: serviceProductFulfillmentDetail}).and(z.record(z.string(), z.unknown())); export const assetType = z.enum(["CheckingAccount", "SavingsAccount", "Investment", "MoneyMarketFund", "Other"]); export const assetDetail = z.object({AssetUniqueIdentifier: z.string(), AssetAccountIdentifier: z.string(), AssetAsOfDate: z.string(), AssetDescription: z.string().nullable(), AssetAvailableBalanceAmount: z.number(), AssetCurrentBalanceAmount: z.number(), AssetHoldingBalanceAmount: z.number().nullable().optional(), AssetHoldingBalanceNetMarginAmount: z.number().nullable().optional(), AssetBondsBalanceAmount: z.number().nullable().optional(), AssetStocksBalanceAmount: z.number().nullable().optional(), AssetCryptoBalanceAmount: z.number().nullable().optional(), AssetOtherBalanceAmount: z.number().nullable().optional(), AssetMarginAmountBalance: z.number().nullable().optional(), AssetAvailableCashBalanceAmount: z.number().nullable().optional(), AssetCashBalanceAmount: z.number().nullable().optional(), AssetType: assetType, AssetTypeAdditionalDescription: z.string().nullable(), AssetDaysRequestedCount: z.number().int(), AssetOwnershipType: z.string().nullable(), AssetRetirementIndicator: z.enum(["Yes", "No"]).nullable().optional(), AssetEmployerSponsoredIndicator: z.enum(["Yes", "No"]).nullable().optional()}).and(z.record(z.string(), z.unknown())); export const assetOwner = z.object({AssetOwnerText: z.string().nullable()}).and(z.record(z.string(), z.unknown())); export const assetOwners = z.object({ASSET_OWNER: z.array(assetOwner)}).and(z.record(z.string(), z.unknown())); export const assetHolderName = z.object({FullName: z.string()}).and(z.record(z.string(), z.unknown())); export const assetHolder = z.object({NAME: assetHolderName}).and(z.record(z.string(), z.unknown())); export const assetTransactionType = z.enum(["Credit", "Debit"]); export const assetInvestmentTransactionType = z.enum(["Buy", "Sell", "Dividends", "Interest", "Transfers", "Reinvestments", "FundsReceived", "Other"]).nullable(); export const assetTransactionCategoryType = z.enum(["ATMFee", "Advertising", "AirTravel", "AlcoholBars", "Allowance", "Amusement", "Arts", "AutoTransport", "AutoInsurance", "AutoPayment", "BabySupplies", "BabysitterDaycare", "BankFee", "BillsUtilities", "Bonus", "BooksSupplies", "Business Services", "Buy", "CashATM", "Charity", "Check", "ChildSupport", "Clothing", "CoffeeShops", "CreditCardPayment", "Dentist", "Doctor", "Education", "ElectronicsSoftware", "Entertainment", "Eyecare", "FastFood", "FederalTax", "FeesCharges", "FinanceCharge", "Financial", "FinancialAdvisor", "FoodDining", "Furnishings", "GasFuel", "GiftsDonations", "Groceries", "Gym", "Hair", "HealthFitness", "HealthInsurance", "Hobbies", "Home", "HomeImprovement", "HomeInsurance", "HomePhone", "HomeServices", "HomeSupplies", "Hotel", "Income", "InterestIncome", "Internet", "Investments", "Kids", "KidsActivities", "LateFee", "Laundry", "LawnGarden", "Legal", "LifeInsurance", "LoanInsurance", "LoanPayment", "Loans", "MobilePhone", "MortgageRent", "MoviesDVDs", "Music", "NewspapersMagazines", "OfficeSupplies", "Parking", "Paycheck", "PersonalCare", "PetFoodSupplies", "PetGrooming", "Pets", "Pharmacy", "Printing", "Property Tax", "Public Transportation", "Reimbursement", "RentalCarTaxi", "Restaurants", "SalesTax", "ServiceParts", "ServiceFee", "Shipping", "Shopping", "SpaMassage", "SportingGoods", "Sports", "StateTax", "Student Loan", "Taxes", "Television", "Toys", "Transfer", "Travel", "Tuition", "Uncategorized", "Utilities", "Vacation", "Veterinary"]).nullable(); export const assetTransactionDetail = z.object({AssetTransactionUniqueIdentifier: z.string(), AssetTransactionAmount: z.number(), AssetTransactionDate: z.string(), AssetTransactionPostDate: z.string(), AssetTransactionType: assetTransactionType, AssetInvestmentTransactionType: assetInvestmentTransactionType.optional(), AssetTransactionPaidByName: z.string().nullable(), AssetTransactionPaidToName: z.string().nullable().optional(), AssetTransactionTypeAdditionalDescription: z.string().nullable(), AssetInvestmentTransactionTypeDescription: z.string().nullable(), AssetTransactionCategoryType: assetTransactionCategoryType, FinancialInstitutionTransactionIdentifier: z.string().nullable()}).and(z.record(z.string(), z.unknown())); export const assetTransactionDescription = z.object({AssetTransactionDescription: z.string()}).and(z.record(z.string(), z.unknown())); export const assetTransaction = z.object({ASSET_TRANSACTION_DETAIL: assetTransactionDetail, ASSET_TRANSACTION_DESCRIPTON: z.array(assetTransactionDescription)}).and(z.record(z.string(), z.unknown())); export const assetTransactions = z.object({ASSET_TRANSACTION: z.array(assetTransaction)}).and(z.record(z.string(), z.unknown())); export const validationSource = z.object({ValidationSourceName: z.string().nullable(), ValidationSourceReferenceIdentifier: z.string().nullable()}).and(z.record(z.string(), z.unknown())); export const validationSources = z.object({VALIDATION_SOURCE: z.array(validationSource)}).and(z.record(z.string(), z.unknown())); export const asset = z.object({ASSET_DETAIL: assetDetail, ASSET_OWNERS: assetOwners, ASSET_HOLDER: assetHolder, ASSET_TRANSACTIONS: assetTransactions, VALIDATION_SOURCES: validationSources}).and(z.record(z.string(), z.unknown())); export const assets = z.object({ASSET: z.array(asset)}).and(z.record(z.string(), z.unknown())); export const verificationOfAssetResponse = z.object({ASSETS: assets}).and(z.record(z.string(), z.unknown())); export const verificationOfAsset = z.object({REPORTING_INFORMATION: reportingInformation, SERVICE_PRODUCT_FULFILLMENT: serviceProductFulfillment, VERIFICATION_OF_ASSET_RESPONSE: verificationOfAssetResponse}).and(z.record(z.string(), z.unknown())); export const status = z.object({StatusCode: z.string().nullable(), StatusDescription: z.string().nullable()}).and(z.record(z.string(), z.unknown())); export const statuses = z.object({STATUS: status}).and(z.record(z.string(), z.unknown())); export const service = z.object({VERIFICATION_OF_ASSET: verificationOfAsset, STATUSES: statuses}).and(z.record(z.string(), z.unknown())); export const services = z.object({SERVICE: service}).and(z.record(z.string(), z.unknown())); export const assetReportFreddie = z.object({LOANS: loans, PARTIES: parties, SERVICES: services}).and(z.record(z.string(), z.unknown())); export const requestID = z.string(); export const assetReportFreddieGetResponse = z.object({DEAL: assetReportFreddie, request_id: requestID, SchemaVersion: z.number()}).and(z.record(z.string(), z.unknown())); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Retrieve an Asset Report with Freddie Mac format. Only Freddie Mac can use this endpoint. * * POST /credit/asset_report/freddie_mac/get */ export const createApiCreditAssetReportFreddieMacGet = async (args: CreateApiCreditAssetReportFreddieMacGetArgs, options?: CreateApiCreditAssetReportFreddieMacGetOptions) => { const {baseUrl = "https://production.plaid.com", fetch: fetchFn = fetch, clientId, secret, plaidVersion} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (clientId !== undefined) headers.set("PLAID-CLIENT-ID", clientId) if (secret !== undefined) headers.set("PLAID-SECRET", secret) if (plaidVersion !== undefined) headers.set("Plaid-Version", plaidVersion) const url = `${baseUrl.replace(/\/+$/, '')}/credit/asset_report/freddie_mac/get${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(assetReportFreddieGetResponse, await res.json()) };