import {z} from 'zod' export type KybApplicationStatus = 'pending' | 'approved' | 'rfi' | 'rejected' | 'superseded'; export type SandboxKybStatusRequest = {status: KybApplicationStatus, comment?: (string | null) | undefined}; export type CreateApiSandboxKybStatusArgs = {'X-Hevn-Account'?: (string | null) | undefined, body: SandboxKybStatusRequest}; export type CreateApiSandboxKybStatusOptions = {baseUrl: string, HTTPBearer?: string | undefined, fetch?: typeof fetch | undefined}; export const platformKybStatus = z.enum(["notStarted", "pending", "approved", "rfi", "rejected"]); export const platformKybApplicationResponse = z.object({id: z.string(), attemptNo: z.number().int(), status: platformKybStatus, comment: z.string().nullable().optional(), submittedAt: z.string(), decidedAt: z.string().nullable().optional()}); export const platformKybAddressView = z.object({streetAddress: z.string().nullable().optional(), addressLine2: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), zip: z.string().nullable().optional(), country: z.string().nullable().optional()}); export const fiatIdentityDocumentType = z.enum(["passport", "id_card", "drivers_license", "driver_license", "residence_permit", "government_id", "state_or_provincial_id", "visa"]); export const platformKybIdentityDocumentView = z.object({type: fiatIdentityDocumentType.nullable().optional(), issuingCountry: z.string().nullable().optional(), number: z.string().nullable().optional()}); export const platformKybCompanyView = z.object({legalName: z.string().nullable().optional(), tradeName: z.string().nullable().optional(), country: z.string().nullable().optional(), registrationNumber: z.string().nullable().optional(), linkedinUrl: z.string().nullable().optional(), taxId: z.string().nullable().optional(), phone: z.string().nullable().optional(), email: z.string().nullable().optional(), address: platformKybAddressView.nullable().optional(), entityType: z.string().nullable().optional(), entityTypeDescription: z.string().nullable().optional(), incorporationDate: z.string().nullable().optional(), industry: z.string().nullable().optional(), websites: z.array(z.string()).nullable().optional(), operatingCountries: z.array(z.string()).nullable().optional(), expectedMonthlyVolumeUsd: z.string().nullable().optional(), expectedCryptoMonthlyVolumeUsd: z.string().nullable().optional(), expectedMonthlyAchUsd: z.string().nullable().optional(), expectedMonthlyWireUsd: z.string().nullable().optional(), cryptoIndustrySubtype: z.string().nullable().optional(), hasIntermediateEntityOwnership: z.boolean().nullable().optional(), primaryTargetMarket: z.string().nullable().optional(), estimatedAnnualRevenue: z.string().nullable().optional(), hasUsBank: z.boolean().nullable().optional(), deniedUsBank: z.boolean().nullable().optional(), hasPep: z.boolean().nullable().optional(), bizAdverse: z.array(z.string()).nullable().optional(), ownersAdverse: z.array(z.string()).nullable().optional(), publicListed: z.boolean().nullable().optional(), txPerMonth: z.string().nullable().optional(), usesBlockchain: z.boolean().nullable().optional(), revenueCovers: z.string().nullable().optional(), institutionalInvestors: z.boolean().nullable().optional(), isRegulated: z.boolean().nullable().optional(), regulatorCountry: z.string().nullable().optional(), regulatoryLicenseType: z.string().nullable().optional(), verifiesCustomerIdentity: z.boolean().nullable().optional(), hasTransactionMonitoring: z.boolean().nullable().optional(), productsServices: z.array(z.string()).nullable().optional(), businessDescription: z.string().nullable().optional(), businessType: z.string().nullable().optional(), businessIndustry: z.string().nullable().optional(), verifierRole: z.string().nullable().optional(), sourceOfFunds: z.string().nullable().optional(), sourceOfFundsDetails: z.string().nullable().optional(), purposeOfFunds: z.string().nullable().optional(), purposeOfFundsDetails: z.string().nullable().optional(), flowOfFunds: z.string().nullable().optional(), complianceScreening: z.string().nullable().optional(), moneyServicesDescription: z.string().nullable().optional(), handlesCustomerFunds: z.boolean().nullable().optional(), actingAsIntermediary: z.boolean().nullable().optional(), sanctionedCountryOperations: z.boolean().nullable().optional(), highRiskActivities: z.array(z.string()).nullable().optional(), depositMethods: z.array(z.string()).nullable().optional(), contactInfo: z.string().nullable().optional(), identityDocuments: z.array(platformKybIdentityDocumentView).optional()}); export const documentType = z.enum(["certificate_of_incorporation", "certificate_of_registration", "formation_document", "articles_of_incorporation", "power_of_attorney", "corporate_structure", "org_structure_chart", "company_registry_extract", "corporate_registry_extract", "shareholder_registry", "cap_table", "director_structure", "proof_of_address", "utility_bill", "aml_policy", "annual_financial_statements", "articles_of_association", "audit_report", "independent_aml_audit_report", "identity_verification_attestation", "institutional_investor_documentation", "audited_financial_statement", "bank_reference_letter", "bank_verification_letter", "bank_statement", "banking_details", "birth_certificate", "business_license", "operating_license", "business_plan", "certificate_of_good_standing", "corporate_resolution", "proof_of_registered_address", "proof_of_operating_address", "crypto_statement", "credit_report", "criminal_record_check", "cv_resume", "driving_license", "education_certificate", "financial_projections", "investment_statement", "insurance_policy", "lease_agreement", "marriage_certificate", "national_id", "national_id_front", "national_id_back", "operating_agreement", "partnership_agreement", "professional_license", "regulatory_license", "reference_letter", "register_of_members", "residence_permit", "shareholding_structure", "shareholders_agreement", "social_security_document", "source_of_funds_declaration", "tax_identification_document", "ein_letter", "tax_certificate", "tax_compliance_form", "tax_returns", "payslip", "pitch_deck", "savings_statement", "trade_references", "vat_registration", "vendor_contracts", "visa", "wealth_statement", "contract", "invoice", "loan_agreement", "subscription_agreement", "safe_agreement", "convertible_note", "promissory_note", "marketing_material", "employment_contract", "authorization_to_act", "authority_to_bind", "ubo_declaration", "income_verification_letter", "source_of_funds", "flow_of_funds", "payroll_register", "investment_agreement", "source_of_wealth", "supporting_document", "memorandum", "passport_front", "passport_back", "id_card_front", "id_card_back", "drivers_license_front", "drivers_license_back", "residence_permit_front", "residence_permit_back", "other"]); export const platformKybDocumentView = z.object({slot: documentType, uploadId: z.string()}); export const platformKybPersonView = z.object({key: z.string().nullable().optional(), id: z.string(), firstName: z.string().nullable().optional(), middleName: z.string().nullable().optional(), lastName: z.string().nullable().optional(), dateOfBirth: z.string().nullable().optional(), birthCountry: z.string().nullable().optional(), nationalities: z.array(z.string()).nullable().optional(), title: z.string().nullable().optional(), country: z.string().nullable().optional(), linkedinUrl: z.string().nullable().optional(), email: z.string().nullable().optional(), phone: z.string().nullable().optional(), address: platformKybAddressView.nullable().optional(), taxIdCountry: z.string().nullable().optional(), taxId: z.string().nullable().optional(), identityDocuments: z.array(platformKybIdentityDocumentView).optional(), ownershipPercentage: z.string().nullable().optional(), hasControl: z.boolean().optional(), isDirector: z.boolean().optional(), isSigner: z.boolean().optional(), documents: z.array(platformKybDocumentView).optional()}); export const platformKybDocumentBody = z.object({company: platformKybCompanyView.nullable().optional(), people: z.array(platformKybPersonView).optional(), documents: z.array(platformKybDocumentView).optional()}); export const platformKybResponse = z.object({ready: z.boolean(), missing: z.array(z.string()).optional(), status: platformKybStatus.optional(), application: platformKybApplicationResponse.nullable().optional(), document: platformKybDocumentBody.optional()}); 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 }; /** * Decide the client's latest KYB application, as the compliance reviewer would * * POST /sandbox/kyb/status */ export const createApiSandboxKybStatus = async (args: CreateApiSandboxKybStatusArgs, options: CreateApiSandboxKybStatusOptions) => { const {baseUrl, fetch: fetchFn = fetch, HTTPBearer} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['X-Hevn-Account'] != null) headers.set("X-Hevn-Account", String(args['X-Hevn-Account'])) if (HTTPBearer !== undefined) headers.set('authorization', `Bearer ${HTTPBearer}`) const url = `${baseUrl.replace(/\/+$/, '')}/sandbox/kyb/status${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(platformKybResponse, await res.json()) };