import {z} from 'zod' export type CreateApiSearchArgs = {body: {email: string, selectedProviders?: (Array<'linkedin' | 'teams' | 'microsoft' | 'google' | 'tiktok' | 'github' | 'gitlab' | 'vsco' | 'data-breach' | 'gravatar' | 'duolingo' | 'etsy' | 'flickr' | 'chess.com' | 'adobe' | 'registered-accounts' | 'email-service' | 'email-pattern' | 'website'>) | undefined, excludedProviders?: (Array<'linkedin' | 'teams' | 'microsoft' | 'google' | 'tiktok' | 'github' | 'gitlab' | 'vsco' | 'data-breach' | 'gravatar' | 'duolingo' | 'etsy' | 'flickr' | 'chess.com' | 'adobe' | 'registered-accounts' | 'email-service' | 'email-pattern' | 'website'>) | undefined}}; export type CreateApiSearchOptions = {baseUrl?: string | undefined, apiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const createApiSearchResponse = z.object({data: z.object({status: z.enum(["found", "not_found"]), profile: z.object({linkedIn: z.object({basic: z.object({displayName: z.string(), initials: z.string(), firstName: z.string(), lastName: z.string(), headline: z.string().optional(), location: z.string(), photoUrl: z.string().optional(), summary: z.string().optional()}).optional(), employment: z.object({positions: z.array(z.object({title: z.string().optional(), companyName: z.string(), companyLocation: z.string().optional(), companyLinkedInUrl: z.string().optional(), companyLogoUrl: z.string().optional(), dateRange: z.object({start: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()}), end: z.union([z.object({kind: z.literal("current")}), z.object({kind: z.literal("date"), value: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()})})]).optional()}).optional(), description: z.string().optional()})), latestPosition: z.object({title: z.string().optional(), companyName: z.string(), companyLocation: z.string().optional(), companyLinkedInUrl: z.string().optional(), companyLogoUrl: z.string().optional(), dateRange: z.object({start: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()}), end: z.union([z.object({kind: z.literal("current")}), z.object({kind: z.literal("date"), value: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()})})]).optional()}).optional(), description: z.string().optional()}).optional(), totalCount: z.number().int().gte(0).lte(9007199254740991).optional()}).optional(), education: z.object({educations: z.array(z.object({schoolName: z.string(), schoolLocation: z.string().optional(), schoolLogoUrl: z.string().optional(), degreeName: z.string().optional(), fieldOfStudy: z.string().optional(), dateRange: z.object({start: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()}), end: z.union([z.object({kind: z.literal("current")}), z.object({kind: z.literal("date"), value: z.object({year: z.number().int().gte(-9007199254740991).lte(9007199254740991), month: z.number().int().gte(1).lte(12).optional(), day: z.number().int().gte(1).lte(31).optional()})})]).optional()}).optional()}))}).optional(), skills: z.object({skills: z.array(z.string()), totalCount: z.number().int().gte(0).lte(9007199254740991).optional()}).optional(), socialGraph: z.object({connectionCount: z.number().int().gte(0).lte(9007199254740991), connectionCountExceedsMax: z.boolean()}).optional(), metadata: z.object({linkedInUrl: z.string().optional()}).optional()}).optional(), teams: z.object({displayName: z.string().optional(), initials: z.string().optional(), firstName: z.string().optional(), lastName: z.string().optional(), photoUrl: z.string().optional(), userType: z.string().optional(), cid: z.string().optional(), corporationName: z.string().optional()}).optional(), microsoft: z.object({displayName: z.string().optional(), initials: z.string().optional(), firstName: z.string().optional(), lastName: z.string().optional(), cid: z.string().optional(), country: z.string().optional(), creationDate: z.string().optional(), lastSeenDate: z.string().optional(), nameLastUpdated: z.string().optional(), profileLastUpdated: z.string().optional(), recoveryEmails: z.array(z.string()).optional(), recoveryPhones: z.array(z.string()).optional(), region: z.string().optional(), liveAccount: z.boolean().optional()}).optional(), vsco: z.object({username: z.string().optional(), profileUrl: z.string().optional(), avatarUrl: z.string().optional(), displayName: z.string().optional(), firstName: z.string().optional(), lastName: z.string().optional(), bio: z.string().optional(), website: z.string().optional(), profileId: z.string().optional(), userId: z.string().optional(), joinedAt: z.string().optional(), lastUploadAt: z.string().optional(), isBrand: z.boolean().optional(), status: z.string().optional(), photos: z.object({count: z.number().int().gte(0).lte(9007199254740991), photos: z.array(z.object({id: z.string(), url: z.string(), date: z.string(), isHidden: z.boolean().optional()}))}).optional()}).optional(), google: z.object({person: z.object({personId: z.string(), personName: z.string().optional(), mapsProfileUrl: z.string(), lastUpdated: z.string().optional(), userType: z.string(), photoUrl: z.string().optional(), contentRestriction: z.string().optional(), isEnterpriseUser: z.boolean().optional()}).optional(), apps: z.object({count: z.number().int().gte(0).lte(9007199254740991), apps: z.array(z.object({name: z.string(), formattedName: z.string()}))}).optional(), photos: z.object({count: z.number().int().gte(0).lte(9007199254740991), photos: z.array(z.object({id: z.string(), url: z.string(), date: z.union([z.string(), z.string()]).optional(), location: z.object({name: z.string(), url: z.string().optional(), address: z.string(), latitude: z.number().optional(), longitude: z.number().optional()}).optional(), isHidden: z.boolean().optional(), rating: z.number().optional(), comment: z.string().optional(), timestamp: z.number().optional(), ownerReply: z.string().optional(), ownerReplyDate: z.string().optional(), mapsUrl: z.string().optional(), linkedReview: z.object({id: z.string(), url: z.string(), rating: z.number().optional(), comment: z.string().optional(), date: z.union([z.string(), z.string()]).optional(), timestamp: z.number().optional(), ownerReply: z.string().optional(), ownerReplyDate: z.string().optional(), location: z.object({name: z.string(), url: z.string().optional(), address: z.string(), latitude: z.number().optional(), longitude: z.number().optional()}).optional()}).optional()}))}).optional(), reviews: z.object({count: z.number().int().gte(0).lte(9007199254740991), reviews: z.array(z.object({id: z.string(), url: z.string(), date: z.union([z.string(), z.string()]).optional(), location: z.object({name: z.string(), url: z.string().optional(), address: z.string(), latitude: z.number().optional(), longitude: z.number().optional()}).optional(), isHidden: z.boolean().optional(), rating: z.number().optional(), comment: z.string().optional(), timestamp: z.number().optional(), ownerReply: z.string().optional(), ownerReplyDate: z.string().optional(), mapsUrl: z.string().optional(), linkedReview: z.object({id: z.string(), url: z.string(), rating: z.number().optional(), comment: z.string().optional(), date: z.union([z.string(), z.string()]).optional(), timestamp: z.number().optional(), ownerReply: z.string().optional(), ownerReplyDate: z.string().optional(), location: z.object({name: z.string(), url: z.string().optional(), address: z.string(), latitude: z.number().optional(), longitude: z.number().optional()}).optional()}).optional()}))}).optional()}).optional(), tiktok: z.object({name: z.string(), username: z.string(), bio: z.string().optional(), language: z.string().optional(), photoUrl: z.string().optional(), profileUrl: z.string().optional(), createdAt: z.string(), profileLastUpdated: z.string().optional(), followers: z.number().int().gte(0).lte(9007199254740991), following: z.number().int().gte(0).lte(9007199254740991), videos: z.number().int().gte(0).lte(9007199254740991), likes: z.number().int().gte(0).lte(9007199254740991), friends: z.number().int().gte(0).lte(9007199254740991)}).optional(), github: z.object({id: z.number().int().gte(0).lte(9007199254740991).optional(), name: z.string().optional(), username: z.string(), location: z.string().optional(), bio: z.string().optional(), company: z.string().optional(), followers: z.number().int().gte(0).lte(9007199254740991).optional(), following: z.number().int().gte(0).lte(9007199254740991).optional(), photoUrl: z.string().optional(), profileUrl: z.string().optional()}).optional(), gitlab: z.object({id: z.number().int().gte(0).lte(9007199254740991).optional(), name: z.string().optional(), username: z.string(), location: z.string().optional(), bio: z.string().optional(), company: z.string().optional(), followers: z.number().int().gte(0).lte(9007199254740991).optional(), following: z.number().int().gte(0).lte(9007199254740991).optional(), photoUrl: z.string().optional(), profileUrl: z.string().optional(), publicEmail: z.string().optional()}).optional(), dataBreach: z.object({firstAppeared: z.string().optional(), lastAppeared: z.string().optional(), results: z.array(z.object({password: z.string().optional(), username: z.string().optional(), fullName: z.string().optional(), ipAddress: z.string().optional(), phoneNumber: z.string().optional(), source: z.object({name: z.string(), date: z.string().optional()}), isHidden: z.boolean().optional()}))}).optional(), registeredAccounts: z.object({count: z.number().int().gte(0).lte(9007199254740991), accounts: z.array(z.object({name: z.string(), formattedName: z.string(), isRegistered: z.boolean()}))}).optional(), gravatar: z.object({username: z.string(), displayName: z.string().optional(), photoUrl: z.string(), profileUrl: z.string(), location: z.string().optional()}).optional(), duolingo: z.object({id: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), name: z.string().optional(), username: z.string().optional(), photoUrl: z.string().optional(), courses: z.array(z.object({language: z.string(), fromLanguage: z.string().optional(), xp: z.number().int().gte(-9007199254740991).lte(9007199254740991)})).optional(), hasPlus: z.boolean().optional(), createdAt: z.string().optional(), totalXp: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), currentStreak: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), longestStreak: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), profileUrl: z.string().optional(), googleLinked: z.boolean().optional(), facebookLinked: z.boolean().optional(), location: z.string().optional(), learningLanguage: z.string().optional(), fromLanguage: z.string().optional(), roles: z.array(z.string()).optional(), hasRecentActivity: z.boolean().optional(), totalLanguages: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional(), streakData: z.object({current: z.object({startDate: z.string().optional(), endDate: z.string().optional(), lastExtendedDate: z.string().optional(), length: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional()}).optional(), previous: z.object({startDate: z.string().optional(), endDate: z.string().optional(), lastExtendedDate: z.string().optional(), length: z.number().int().gte(-9007199254740991).lte(9007199254740991).optional()}).optional()}).optional()}).optional(), etsy: z.object({userId: z.number().int().gte(0).lte(9007199254740991).optional(), realName: z.string().optional(), username: z.string().optional(), gender: z.string().optional(), location: z.string().optional(), bio: z.string().optional(), isSeller: z.boolean().optional(), hasPage: z.boolean().optional(), followerCount: z.number().int().gte(0).lte(9007199254740991).optional(), followingCount: z.number().int().gte(0).lte(9007199254740991).optional(), numFavorites: z.number().int().gte(0).lte(9007199254740991).optional(), avatarUrl: z.string().optional(), profileUrl: z.string().optional(), createdAt: z.string().optional(), updatedAt: z.string().optional()}).optional(), chess: z.object({userId: z.number().int().gte(0).lte(9007199254740991).optional(), username: z.string(), fullName: z.string().optional(), firstName: z.string().optional(), lastName: z.string().optional(), avatarUrl: z.string().optional(), profileUrl: z.string().optional(), country: z.string().optional(), lastLoginDate: z.string().optional(), memberSince: z.string().optional()}).optional(), flickr: z.object({nsid: z.string(), username: z.string().optional(), realname: z.string().optional(), gender: z.enum(["male", "female"]).optional(), location: z.string().optional(), description: z.string().optional(), profileUrl: z.string().optional(), photosUrl: z.string().optional(), website: z.string().optional(), avatarUrl: z.string().optional(), coverPhotoUrl: z.string().optional(), joinedAt: z.string().optional(), contacts: z.number().int().gte(0).lte(9007199254740991).optional(), reverseContacts: z.number().int().gte(0).lte(9007199254740991).optional(), favoritesCount: z.number().int().gte(0).lte(9007199254740991).optional(), groupsCount: z.number().int().gte(0).lte(9007199254740991).optional(), isPro: z.boolean().optional(), photos: z.object({count: z.number().int().gte(0).lte(9007199254740991), photos: z.array(z.object({id: z.string(), title: z.string().optional(), description: z.string().optional(), dateTaken: z.string().optional(), dateUploaded: z.string().optional(), viewCount: z.number().int().gte(0).lte(9007199254740991).optional(), faveCount: z.number().int().gte(0).lte(9007199254740991).optional(), commentCount: z.number().int().gte(0).lte(9007199254740991).optional(), media: z.string().optional(), urlLarge: z.string().optional(), urlOriginal: z.string().optional(), urlThumb: z.string().optional()}))}).optional()}).optional(), adobe: z.object({accountType: z.string().optional(), status: z.string().optional(), avatarUrl: z.string().optional(), hasT2ELinked: z.boolean().optional(), authenticationMethods: z.array(z.string()).optional()}).optional(), emailService: z.object({canReceiveEmail: z.boolean(), emailServiceProvider: z.enum(["gmail", "outlook", "yahoo", "proton", "icloud", "xfinity", "cloudflare", "mail.com", "hostinger", "proofpoint", "orange", "free", "laposte", "sfr", "gmx", "web.de", "yandex", "libero", "qq"]).optional(), mxHosts: z.array(z.string()).optional()}).optional(), emailPattern: z.object({pattern: z.string(), nameParts: z.object({firstName: z.string().optional(), lastName: z.string().optional(), firstInitial: z.string().optional(), lastInitial: z.string().optional()}).optional()}).optional(), website: z.object({url: z.string(), title: z.string(), description: z.string().optional(), iconUrl: z.string().optional()}).optional(), summary: z.object({names: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), usernames: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), profilePictures: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), photos: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), links: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), locations: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), dates: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional(), phoneNumbers: z.array(z.object({value: z.string(), source: z.string(), formattedSource: z.string(), url: z.string().optional(), label: z.string().optional()})).optional()}).optional()}).nullable()})}); 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 }; /** * Search for public profile information associated with an email address. * * POST /search */ export const createApiSearch = async (args: CreateApiSearchArgs, options?: CreateApiSearchOptions) => { const {baseUrl = "https://api.behindtheemail.com/v1", fetch: fetchFn = fetch, apiKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (apiKey !== undefined) headers.set('authorization', `Bearer ${apiKey}`) const url = `${baseUrl.replace(/\/+$/, '')}/search${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(createApiSearchResponse, await res.json()) };