import {z} from 'zod' export type SavedSearchType = 'companies' | 'jobs'; export type GetApiV0SavedSearchesArgs = {user_ids?: Array | undefined, types?: (Array | null) | undefined, order_by?: ('name' | 'created_at' | 'updated_at') | undefined, order_direction?: ('asc' | 'desc') | undefined}; export type GetApiV0SavedSearchesOptions = {baseUrl?: string | undefined, Bearer?: string | undefined, fetch?: typeof fetch | undefined}; export const savedSearchType = z.enum(["companies", "jobs"]); export const columnSortCompanySearch = z.object({desc: z.boolean().optional(), field: z.enum(["relevance", "name", "num_jobs", "num_jobs_last_30_days", "num_jobs_found", "employee_count", "alexa_ranking", "founded_year", "annual_revenue_usd", "total_funding_usd", "last_funding_round_date", "confidence", "jobs", "first_date_found"])}); export const workplaceType = z.enum(["on_site", "hybrid", "remote"]); export const jobLocationFilter = z.object({id: z.number().int().nullable().optional()}); export const commitment = z.enum(["full_time", "part_time", "temporary", "internship", "contract", "freelance", "co_founder", "apprenticeship", "seasonal", "volunteer", "other"]); export const jobFiltersOutput = z.object({job_title_or: z.array(z.string()).optional(), job_title_not: z.array(z.string()).optional(), job_title_pattern_and: z.array(z.string()).optional(), job_title_pattern_or: z.array(z.string()).optional(), job_title_pattern_not: z.array(z.string()).optional(), job_country_code_or: z.array(z.string()).optional(), job_country_code_not: z.array(z.string()).optional(), posted_at_max_age_days: z.number().int().nullable().optional(), posted_at_gte: z.string().nullable().optional(), posted_at_lte: z.string().nullable().optional(), discovered_at_max_age_days: z.number().int().nullable().optional(), discovered_at_min_age_days: z.number().int().nullable().optional(), discovered_at_gte: z.union([z.string(), z.string()]).nullable().optional(), discovered_at_lte: z.union([z.string(), z.string()]).nullable().optional(), job_description_pattern_or: z.array(z.string()).optional(), job_description_pattern_not: z.array(z.string()).optional(), job_description_pattern_and: z.array(z.string()).optional(), job_description_pattern_is_case_insensitive: z.boolean().nullable().optional(), job_description_contains_or: z.array(z.string()).optional(), job_description_contains_not: z.array(z.string()).optional(), job_description_pattern_case_sensitive_or: z.array(z.string()).optional(), remote: z.boolean().nullable().optional(), workplace_types_or: z.array(workplaceType).nullable().optional(), only_jobs_with_reports_to: z.boolean().nullable().optional(), reports_to_exists: z.boolean().nullable().optional(), final_url_exists: z.boolean().nullable().optional(), only_jobs_with_hiring_managers: z.boolean().nullable().optional(), hiring_managers_exists: z.boolean().nullable().optional(), job_id_or: z.array(z.number().int()).optional(), job_id_not: z.array(z.number().int()).optional(), job_ids: z.array(z.number().int()).optional(), job_seniority_or: z.array(z.enum(["c_level", "staff", "senior", "junior", "mid_level"])).optional(), min_salary_usd: z.number().nullable().optional(), max_salary_usd: z.number().nullable().optional(), job_technology_slug_or: z.array(z.string()).optional(), job_technology_slug_not: z.array(z.string()).optional(), job_technology_slug_and: z.array(z.string()).optional(), job_keyword_slug_or: z.array(z.string()).optional(), job_keyword_slug_and: z.array(z.string()).optional(), job_keyword_slug_not: z.array(z.string()).optional(), job_location_pattern_or: z.array(z.string()).optional(), job_location_pattern_not: z.array(z.string()).optional(), job_location_or: z.array(jobLocationFilter).optional(), job_location_not: z.array(jobLocationFilter).optional(), url_domain_or: z.array(z.string()).optional(), url_domain_not: z.array(z.string()).optional(), scraper_name_pattern_or: z.array(z.string()).optional(), easy_apply: z.boolean().nullable().optional(), easy_apply_or_null: z.boolean().nullable().optional(), employment_statuses_or: z.array(commitment).nullable().optional(), is_closed: z.boolean().nullable().optional(), closed_at_gte: z.string().nullable().optional(), closed_at_lte: z.string().nullable().optional(), property_exists_or: z.array(z.enum(["final_url", "hiring_team", "employment_statuses"])).optional(), property_exists_and: z.array(z.enum(["final_url", "hiring_team", "employment_statuses"])).optional()}); export const keywordFilters = z.object({keyword_slug_or: z.array(z.string()).optional(), keyword_category_slug_or: z.array(z.string()).optional(), keyword_parent_category_slug_or: z.array(z.string()).optional(), technology_slug_or: z.array(z.string()).optional(), technology_category_slug_or: z.array(z.string()).optional(), technology_parent_category_slug_or: z.array(z.string()).optional(), max_rank: z.number().int().nullable().optional(), min_jobs: z.number().int().nullable().optional(), max_jobs: z.number().int().nullable().optional(), min_relative_occurrence: z.number().nullable().optional(), first_date_found_gte: z.string().nullable().optional(), first_date_found_lte: z.string().nullable().optional(), last_date_found_gte: z.string().nullable().optional(), last_date_found_lte: z.string().nullable().optional(), confidence_or: z.array(z.enum(["low", "medium", "high"])).optional()}); export const companySearchFiltersOutput = z.object({expand_technology_slugs: z.array(z.string()).optional(), order_by: z.array(columnSortCompanySearch).optional(), company_name_or: z.array(z.string()).optional(), company_name_case_insensitive_or: z.array(z.string()).optional(), company_id_or: z.array(z.string()).optional(), company_id_not: z.array(z.string()).optional(), company_domain_or: z.array(z.string()).optional(), company_domain_not: z.array(z.string()).optional(), company_name_not: z.array(z.string()).optional(), company_name_partial_match_or: z.array(z.string()).optional(), company_name_partial_match_not: z.array(z.string()).optional(), company_linkedin_url_or: z.array(z.string()).optional(), blur_company_data: z.boolean().optional(), property_exists_or: z.array(z.enum(["domain", "linkedin_url"])).optional(), property_exists_and: z.array(z.enum(["domain", "linkedin_url"])).optional(), offset: z.number().int().gte(0).optional(), page: z.number().int().gte(0).optional(), limit: z.number().int().gte(1).optional(), company_description_pattern_or: z.array(z.string()).optional(), company_description_pattern_not: z.array(z.string()).optional(), company_description_pattern_accent_insensitive: z.boolean().nullable().optional(), min_revenue_usd: z.number().int().nullable().optional(), max_revenue_usd: z.number().int().nullable().optional(), min_revenue_usd_or_null: z.number().int().nullable().optional(), max_revenue_usd_or_null: z.number().int().nullable().optional(), min_employee_count: z.number().int().nullable().optional(), max_employee_count: z.number().int().nullable().optional(), min_employee_count_or_null: z.number().int().nullable().optional(), max_employee_count_or_null: z.number().int().nullable().optional(), min_funding_usd: z.number().int().nullable().optional(), max_funding_usd: z.number().int().nullable().optional(), funding_stage_or: z.array(z.string()).optional(), industry_or: z.array(z.string()).optional(), industry_not: z.array(z.string()).optional(), industry_id_or: z.array(z.number().int()).optional(), industry_id_not: z.array(z.number().int()).optional(), industry_id_not_or_null: z.array(z.number().int()).optional(), company_tags_or: z.array(z.string()).optional(), company_type: z.enum(["recruiting_agency", "direct_employer", "all"]).nullable().optional(), company_investors_or: z.array(z.string()).optional(), company_investors_partial_match_or: z.array(z.string()).optional(), company_technology_slug_or: z.array(z.string()).optional(), company_technology_slug_and: z.array(z.string()).optional(), company_technology_slug_not: z.array(z.string()).optional(), company_keyword_slug_or: z.array(z.string()).optional(), company_keyword_slug_and: z.array(z.string()).optional(), company_keyword_slug_not: z.array(z.string()).optional(), only_yc_companies: z.boolean().nullable().optional(), company_location_pattern_or: z.array(z.string()).optional(), company_country_code_or: z.array(z.string()).optional(), company_country_code_not: z.array(z.string()).optional(), company_country_code_not_or_null: z.array(z.string()).optional(), company_list_id_or: z.array(z.number().int()).optional(), company_list_id_not: z.array(z.number().int()).optional(), company_crm_status_or: z.array(z.string()).optional(), company_crm_status_not: z.array(z.string()).optional(), company_linkedin_url_exists: z.boolean().nullable().optional(), revealed_company_data: z.boolean().nullable().optional(), company_revealed_at_gte: z.string().nullable().optional(), company_revealed_at_lte: z.string().nullable().optional(), company_not_revealed_at_gte: z.string().nullable().optional(), company_not_revealed_at_lte: z.string().nullable().optional(), company_revealed_within_hours: z.number().int().gte(1).lte(87600).nullable().optional(), company_not_revealed_within_hours: z.number().int().gte(1).lte(87600).nullable().optional(), last_funding_round_date_lte: z.string().nullable().optional(), last_funding_round_date_gte: z.string().nullable().optional(), include_total_results: z.boolean().optional(), job_filters: jobFiltersOutput.nullable().optional(), min_num_jobs_found: z.number().int().gte(1).nullable().optional(), tech_filters: keywordFilters.nullable().optional()}); export const columnSortJobSearch = z.object({desc: z.boolean().optional(), field: z.enum(["date_posted", "discovered_at", "salary", "job_title", "company", "num_jobs"]).optional()}); export const jobSearchFiltersOutput = z.object({order_by: z.array(columnSortJobSearch).optional(), offset: z.number().int().gte(0).optional(), page: z.number().int().gte(0).optional(), limit: z.number().int().gte(1).optional(), job_title_or: z.array(z.string()).optional(), job_title_not: z.array(z.string()).optional(), job_title_pattern_and: z.array(z.string()).optional(), job_title_pattern_or: z.array(z.string()).optional(), job_title_pattern_not: z.array(z.string()).optional(), job_country_code_or: z.array(z.string()).optional(), job_country_code_not: z.array(z.string()).optional(), posted_at_max_age_days: z.number().int().nullable().optional(), posted_at_gte: z.string().nullable().optional(), posted_at_lte: z.string().nullable().optional(), discovered_at_max_age_days: z.number().int().nullable().optional(), discovered_at_min_age_days: z.number().int().nullable().optional(), discovered_at_gte: z.union([z.string(), z.string()]).nullable().optional(), discovered_at_lte: z.union([z.string(), z.string()]).nullable().optional(), job_description_pattern_or: z.array(z.string()).optional(), job_description_pattern_not: z.array(z.string()).optional(), job_description_pattern_and: z.array(z.string()).optional(), job_description_pattern_is_case_insensitive: z.boolean().nullable().optional(), job_description_contains_or: z.array(z.string()).optional(), job_description_contains_not: z.array(z.string()).optional(), job_description_pattern_case_sensitive_or: z.array(z.string()).optional(), remote: z.boolean().nullable().optional(), workplace_types_or: z.array(workplaceType).nullable().optional(), only_jobs_with_reports_to: z.boolean().nullable().optional(), reports_to_exists: z.boolean().nullable().optional(), final_url_exists: z.boolean().nullable().optional(), only_jobs_with_hiring_managers: z.boolean().nullable().optional(), hiring_managers_exists: z.boolean().nullable().optional(), job_id_or: z.array(z.number().int()).optional(), job_id_not: z.array(z.number().int()).optional(), job_ids: z.array(z.number().int()).optional(), job_seniority_or: z.array(z.enum(["c_level", "staff", "senior", "junior", "mid_level"])).optional(), min_salary_usd: z.number().nullable().optional(), max_salary_usd: z.number().nullable().optional(), job_technology_slug_or: z.array(z.string()).optional(), job_technology_slug_not: z.array(z.string()).optional(), job_technology_slug_and: z.array(z.string()).optional(), job_keyword_slug_or: z.array(z.string()).optional(), job_keyword_slug_and: z.array(z.string()).optional(), job_keyword_slug_not: z.array(z.string()).optional(), job_location_pattern_or: z.array(z.string()).optional(), job_location_pattern_not: z.array(z.string()).optional(), job_location_or: z.array(jobLocationFilter).optional(), job_location_not: z.array(jobLocationFilter).optional(), url_domain_or: z.array(z.string()).optional(), url_domain_not: z.array(z.string()).optional(), scraper_name_pattern_or: z.array(z.string()).optional(), easy_apply: z.boolean().nullable().optional(), easy_apply_or_null: z.boolean().nullable().optional(), employment_statuses_or: z.array(commitment).nullable().optional(), is_closed: z.boolean().nullable().optional(), closed_at_gte: z.string().nullable().optional(), closed_at_lte: z.string().nullable().optional(), property_exists_or: z.array(z.enum(["company_object.domain", "company_object.linkedin_url", "final_url", "hiring_team", "employment_statuses"])).optional(), property_exists_and: z.array(z.enum(["company_object.domain", "company_object.linkedin_url", "final_url", "hiring_team", "employment_statuses"])).optional(), company_name_or: z.array(z.string()).optional(), company_name_case_insensitive_or: z.array(z.string()).optional(), company_id_or: z.array(z.string()).optional(), company_id_not: z.array(z.string()).optional(), company_domain_or: z.array(z.string()).optional(), company_domain_not: z.array(z.string()).optional(), company_name_not: z.array(z.string()).optional(), company_name_partial_match_or: z.array(z.string()).optional(), company_name_partial_match_not: z.array(z.string()).optional(), company_linkedin_url_or: z.array(z.string()).optional(), blur_company_data: z.boolean().optional(), company_description_pattern_or: z.array(z.string()).optional(), company_description_pattern_not: z.array(z.string()).optional(), company_description_pattern_accent_insensitive: z.boolean().nullable().optional(), min_revenue_usd: z.number().int().nullable().optional(), max_revenue_usd: z.number().int().nullable().optional(), min_revenue_usd_or_null: z.number().int().nullable().optional(), max_revenue_usd_or_null: z.number().int().nullable().optional(), min_employee_count: z.number().int().nullable().optional(), max_employee_count: z.number().int().nullable().optional(), min_employee_count_or_null: z.number().int().nullable().optional(), max_employee_count_or_null: z.number().int().nullable().optional(), min_funding_usd: z.number().int().nullable().optional(), max_funding_usd: z.number().int().nullable().optional(), funding_stage_or: z.array(z.string()).optional(), industry_or: z.array(z.string()).optional(), industry_not: z.array(z.string()).optional(), industry_id_or: z.array(z.number().int()).optional(), industry_id_not: z.array(z.number().int()).optional(), industry_id_not_or_null: z.array(z.number().int()).optional(), company_tags_or: z.array(z.string()).optional(), company_type: z.enum(["recruiting_agency", "direct_employer", "all"]).nullable().optional(), company_investors_or: z.array(z.string()).optional(), company_investors_partial_match_or: z.array(z.string()).optional(), company_technology_slug_or: z.array(z.string()).optional(), company_technology_slug_and: z.array(z.string()).optional(), company_technology_slug_not: z.array(z.string()).optional(), company_keyword_slug_or: z.array(z.string()).optional(), company_keyword_slug_and: z.array(z.string()).optional(), company_keyword_slug_not: z.array(z.string()).optional(), only_yc_companies: z.boolean().nullable().optional(), company_location_pattern_or: z.array(z.string()).optional(), company_country_code_or: z.array(z.string()).optional(), company_country_code_not: z.array(z.string()).optional(), company_country_code_not_or_null: z.array(z.string()).optional(), company_list_id_or: z.array(z.number().int()).optional(), company_list_id_not: z.array(z.number().int()).optional(), company_crm_status_or: z.array(z.string()).optional(), company_crm_status_not: z.array(z.string()).optional(), company_linkedin_url_exists: z.boolean().nullable().optional(), revealed_company_data: z.boolean().nullable().optional(), company_revealed_at_gte: z.string().nullable().optional(), company_revealed_at_lte: z.string().nullable().optional(), company_not_revealed_at_gte: z.string().nullable().optional(), company_not_revealed_at_lte: z.string().nullable().optional(), company_revealed_within_hours: z.number().int().gte(1).lte(87600).nullable().optional(), company_not_revealed_within_hours: z.number().int().gte(1).lte(87600).nullable().optional(), last_funding_round_date_lte: z.string().nullable().optional(), last_funding_round_date_gte: z.string().nullable().optional(), include_total_results: z.boolean().optional()}); export const userSimple = z.object({id: z.number().int(), email: z.string(), first_name: z.string().nullable().optional(), last_name: z.string().nullable().optional(), picture_url: z.string().nullable().optional()}); export const savedSearchOrigin = z.enum(["manual", "signal"]); export const savedSearchFrequency = z.enum(["daily", "weekly"]); export const savedSearchResponseV0 = z.object({id: z.number().int(), name: z.string(), type: savedSearchType, body: z.union([companySearchFiltersOutput, jobSearchFiltersOutput]), is_alert_active: z.boolean().optional(), user_id: z.number().int(), team_id: z.number().int(), created_at: z.string(), updated_at: z.string(), query: z.string().nullable().optional(), path: z.string().nullable().optional(), querystring_params: z.record(z.string(), z.unknown()).nullable().optional(), creator: userSimple, is_archived: z.boolean().optional(), has_active_webhooks: z.boolean().optional(), origin: savedSearchOrigin.optional(), frequency: savedSearchFrequency.nullable().optional(), emails_subscribed: z.array(z.string()).nullable().optional()}); export const getApiV0SavedSearchesResponse = z.array(savedSearchResponseV0); 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 }; /** * List Saved Searches * * GET /v0/saved_searches */ export const getApiV0SavedSearches = async (args: GetApiV0SavedSearchesArgs, options?: GetApiV0SavedSearchesOptions) => { const {baseUrl = "https://api.theirstack.com", fetch: fetchFn = fetch, Bearer} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") for (const item of args.user_ids ?? []) query.append("user_ids", String(item)) for (const item of args.types ?? []) query.append("types", String(item)) if (args.order_by != null) query.append("order_by", String(args.order_by)) if (args.order_direction != null) query.append("order_direction", String(args.order_direction)) if (Bearer !== undefined) headers.set('authorization', `Bearer ${Bearer}`) const url = `${baseUrl.replace(/\/+$/, '')}/v0/saved_searches${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(getApiV0SavedSearchesResponse, await res.json()) };