import {z} from 'zod' export type PinterestLibPaginationOrder = 'ASCENDING' | 'DESCENDING'; export type CampaignIdType = string; export type PinterestLibIntegerFormatType = string; export type EntityStatus = 'ACTIVE' | 'PAUSED' | 'ARCHIVED' | 'DRAFT' | 'DELETED_DRAFT'; export type GetApiAdAccountsAdAccountIdAdGroupsArgs = {bookmark?: string | undefined, page_size?: number | undefined, order?: PinterestLibPaginationOrder | undefined, ad_account_id: string, campaign_ids?: Array | undefined, ad_group_ids?: Array | undefined, entity_statuses?: Array | undefined, translate_interests_to_names?: boolean | undefined}; export type GetApiAdAccountsAdAccountIdAdGroupsOptions = {baseUrl?: string | undefined, pinterestOauth2?: string | undefined, fetch?: typeof fetch | undefined}; export const bidStrategyType = z.enum(["AUTOMATIC_BID", "MAX_BID", "TARGET_AVG", "null"]).nullable(); export const actionType = z.enum(["CLICKTHROUGH", "IMPRESSION", "VIDEO_V_50_MRC"]); export const conversionLearningModeType = z.enum(["NOT_ACTIVE", "ACTIVE", "null"]).nullable(); export const pinterestLibIntegerFormatType = z.string(); export const adgroupTrackingFeatureType = z.enum(["TRENDS", "CLONE_META", "BULK_EDITOR", "AD_ROTATION"]); export const adgroupTrackingFeatures = z.object({enabled: z.array(adgroupTrackingFeatureType).optional()}).nullable(); export const attributionWindows = z.object({click_window_days: z.number().int().optional(), engagement_window_days: z.number().int().optional(), view_window_days: z.number().int().optional()}); export const conversionEvent = z.enum(["PAGE_VISIT", "SIGNUP", "CHECKOUT", "CUSTOM", "VIEW_CATEGORY", "SEARCH", "ADD_TO_CART", "WATCH_VIDEO", "LEAD", "APP_INSTALL"]); export const conversionTagV3GoalMetadata = z.object({attribution_windows: attributionWindows.optional(), conversion_event: conversionEvent.optional(), conversion_tag_id: z.string().optional(), cpa_goal_value_in_micro_currency: z.string().optional(), is_roas_optimized: z.boolean().nullable().optional(), reporting_event: z.string().optional()}); export const frequencyGoalMetadataTimerange = z.enum(["THIRTY_DAY", "DAY", "SEVEN_DAY", "TWENTY_MINUTE", "TEN_MINUTE", "TWENTY_FOUR_HOUR"]); export const frequencyGoalMetadata = z.object({frequency: z.number().int().gte(2).lte(20).optional(), timerange: frequencyGoalMetadataTimerange.optional()}); export const scrollupGoalMetadata = z.object({scrollup_goal_value_in_micro_currency: z.string().nullable().optional()}); export const nullableOptimizationGoalMetadata = z.object({conversion_tag_v3_goal_metadata: conversionTagV3GoalMetadata.optional(), frequency_goal_metadata: frequencyGoalMetadata.optional(), scrollup_goal_metadata: scrollupGoalMetadata.optional()}).nullable(); export const performancePlusCampaignSettings = z.object({boost_prospecting_ad_group_bid: z.boolean().optional(), pinner_list_exclusions: z.array(z.string()).optional()}).nullable(); export const placementGroupType = z.enum(["ALL", "SEARCH", "BROWSE", "OTHER"]); export const placementTrafficType = z.enum(["ALL", "TWO_COLUMN_FEED", "FULLSCREEN_FEED", "null"]).nullable(); export const promotionApplicationLevel = z.enum(["NONE", "ITEM", "AD_GROUP", "null"]).nullable(); export const entityStatus = z.enum(["ACTIVE", "PAUSED", "ARCHIVED", "DRAFT", "DELETED_DRAFT"]); export const summaryStatus = z.enum(["RUNNING", "PAUSED", "NOT_STARTED", "COMPLETED", "ADVERTISER_DISABLED", "ARCHIVED", "DRAFT", "DELETED_DRAFT"]); export const targetingSpecAgeBucket = z.enum(["18-24", "19+", "20+", "21+", "25-34", "35-44", "45-49", "50-54", "55-64", "65+"]); export const targetingSpecAppType = z.enum(["android_mobile", "android_tablet", "ipad", "iphone", "web", "web_mobile"]); export const targetingSpecGender = z.enum(["unknown", "male", "female"]); export const targetingSpecShoppingRetargeting = z.object({exclusion_window: z.number().int().optional(), lookback_window: z.number().int().optional(), tag_types: z.array(z.number().int()).optional()}); export const targetingStrategy = z.enum(["CHOOSE_YOUR_OWN", "FIND_NEW_CUSTOMERS", "RECONNECT_WITH_USERS"]); export const targetingSpecOptimal = z.object({AGE_BUCKET: z.array(targetingSpecAgeBucket).nullable().optional(), APPTYPE: z.array(targetingSpecAppType).nullable().optional(), AUDIENCE_EXCLUDE: z.array(pinterestLibIntegerFormatType).nullable().optional(), AUDIENCE_INCLUDE: z.array(pinterestLibIntegerFormatType).nullable().optional(), GENDER: z.array(targetingSpecGender).nullable().optional(), GEO: z.array(z.string()).nullable().optional(), GEO_EXCLUDE: z.array(z.string()).optional(), INTEREST: z.array(z.string()).optional(), LOCALE: z.array(z.string()).nullable().optional(), LOCATION: z.array(z.string()).nullable().optional(), LOCATION_EXCLUDE: z.array(z.string()).optional(), MAXIMUM_AGE: z.string().optional(), MINIMUM_AGE: z.string().optional(), SHOPPING_RETARGETING: z.array(targetingSpecShoppingRetargeting).nullable().optional(), TARGETING_STRATEGY: z.array(targetingStrategy).nullable().optional()}); export const adGroupTrackingURLs = z.object({audience_verification: z.array(z.string()).optional(), buyable_button: z.array(z.string()).optional(), click: z.array(z.string()).optional(), engagement: z.array(z.string()).optional(), impression: z.array(z.string()).optional()}).nullable(); export const adGroup = z.object({ad_account_id: z.string().optional(), bid_in_micro_currency: z.number().int().nullable().optional(), bid_strategy_type: bidStrategyType.optional(), billable_event: actionType, budget_in_micro_currency: z.number().int().nullable().optional(), campaign_id: z.string(), conversion_learning_mode_type: conversionLearningModeType, created_time: z.number().int(), customer_segment_id: pinterestLibIntegerFormatType.optional(), dca_assets: z.unknown(), end_time: z.number().int().nullable().optional(), ext_features: adgroupTrackingFeatures.optional(), feed_profile_id: z.string().optional(), id: z.string().max(18), is_creative_optimization: z.boolean().nullable().optional(), is_local_inventory: z.boolean().optional(), lifetime_frequency_cap: z.number().int().optional(), local_inventory_radius_in_miles: z.number().optional(), name: z.string(), optimization_goal_metadata: nullableOptimizationGoalMetadata.nullable().optional(), performance_plus_campaign_settings: performancePlusCampaignSettings.nullable().optional(), placement_group: placementGroupType.optional(), placement_traffic_type: placementTrafficType.nullable().optional(), promotion_application_level: promotionApplicationLevel.nullable().optional(), promotion_id: z.string().nullable().optional(), promotion_ids: z.array(z.string()).optional(), start_time: z.number().int().nullable().optional(), status: entityStatus.optional(), summary_status: summaryStatus, targeting_spec: targetingSpecOptimal.optional(), targeting_template_ids: z.array(pinterestLibIntegerFormatType).nullable().optional(), tracking_urls: adGroupTrackingURLs.optional(), type: z.string(), updated_time: z.number().int()}); export const getApiAdAccountsAdAccountIdAdGroupsResponse = z.object({bookmark: z.string().nullable().optional(), items: z.array(adGroup)}); 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 ad groups * * GET /ad_accounts/{ad_account_id}/ad_groups * * Also accepts auth: client_credentials */ export const getApiAdAccountsAdAccountIdAdGroups = async (args: GetApiAdAccountsAdAccountIdAdGroupsArgs, options?: GetApiAdAccountsAdAccountIdAdGroupsOptions) => { const {baseUrl = "https://api.pinterest.com/v5", fetch: fetchFn = fetch, pinterestOauth2} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.bookmark != null) query.append("bookmark", String(args.bookmark)) if (args.page_size != null) query.append("page_size", String(args.page_size)) if (args.order != null) query.append("order", String(args.order)) for (const item of args.campaign_ids ?? []) query.append("campaign_ids", String(item)) for (const item of args.ad_group_ids ?? []) query.append("ad_group_ids", String(item)) for (const item of args.entity_statuses ?? []) query.append("entity_statuses", String(item)) if (args.translate_interests_to_names != null) query.append("translate_interests_to_names", String(args.translate_interests_to_names)) if (pinterestOauth2 !== undefined) headers.set('authorization', `Bearer ${pinterestOauth2}`) const url = `${baseUrl.replace(/\/+$/, '')}/ad_accounts/${encodeURIComponent(String(args.ad_account_id))}/ad_groups${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(getApiAdAccountsAdAccountIdAdGroupsResponse, await res.json()) };