import {z} from 'zod' export type GetApi30CampaignsArgs = {fields?: Array | undefined, exclude_fields?: Array | undefined, count?: number | undefined, offset?: number | undefined, type?: ('regular' | 'plaintext' | 'absplit' | 'rss' | 'variate') | undefined, status?: ('save' | 'paused' | 'schedule' | 'sending' | 'sent') | undefined, before_send_time?: string | undefined, since_send_time?: string | undefined, before_create_time?: string | undefined, since_create_time?: string | undefined, list_id?: string | undefined, folder_id?: string | undefined, member_id?: string | undefined, sort_field?: ('create_time' | 'send_time') | undefined, sort_dir?: ('ASC' | 'DESC') | undefined, include_resend_shortcut_eligibility?: boolean | undefined, include_resend_shortcut_usage?: boolean | undefined}; export type GetApi30CampaignsOptions = {baseUrl?: string | undefined, bearerToken?: string | undefined, fetch?: typeof fetch | undefined}; export const getApi30CampaignsResponse = z.object({_links: z.array(z.object({href: z.string().optional(), method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).optional(), rel: z.string().optional(), schema: z.string().optional(), targetSchema: z.string().optional()})).optional(), campaigns: z.array(z.object({_links: z.array(z.object({href: z.string().optional(), method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]).optional(), rel: z.string().optional(), schema: z.string().optional(), targetSchema: z.string().optional()})).optional(), ab_split_opts: z.object({from_name_a: z.string().optional(), from_name_b: z.string().optional(), pick_winner: z.enum(["opens", "clicks", "manual"]).optional(), reply_email_a: z.string().optional(), reply_email_b: z.string().optional(), send_time_a: z.string().optional(), send_time_b: z.string().optional(), send_time_winner: z.string().optional(), split_size: z.number().int().gte(1).lte(50).optional(), split_test: z.enum(["subject", "from_name", "schedule"]).optional(), subject_a: z.string().optional(), subject_b: z.string().optional(), wait_time: z.number().int().optional(), wait_units: z.enum(["hours", "days"]).optional()}).optional(), archive_url: z.string().optional(), content_type: z.enum(["template", "html", "url", "multichannel"]).optional(), create_time: z.string().optional(), delivery_status: z.object({can_cancel: z.boolean().optional(), emails_canceled: z.number().int().optional(), emails_sent: z.number().int().optional(), enabled: z.boolean().optional(), status: z.enum(["delivering", "delivered", "canceling", "canceled"]).optional()}).optional(), emails_sent: z.number().int().optional(), id: z.string().optional(), long_archive_url: z.string().optional(), needs_block_refresh: z.boolean().optional(), parent_campaign_id: z.string().optional(), recipients: z.object({list_id: z.string().optional(), list_is_active: z.boolean().optional(), list_name: z.string().optional(), recipient_count: z.number().int().optional(), segment_opts: z.object({conditions: z.array(z.union([z.object({condition_type: z.literal("Aim").optional(), field: z.literal("aim").optional(), op: z.enum(["open", "click", "sent", "noopen", "noclick", "nosent"]).optional(), value: z.string().optional()}), z.object({condition_type: z.literal("Automation").optional(), field: z.literal("automation"), op: z.enum(["started", "completed", "not_started", "not_completed"]), value: z.string()}), z.object({condition_type: z.literal("CampaignPoll").optional(), field: z.literal("poll"), op: z.enum(["member", "notmember"]), value: z.number()}), z.object({condition_type: z.literal("Conversation").optional(), field: z.literal("conversation"), op: z.enum(["member", "notmember"]), value: z.string()}), z.object({condition_type: z.literal("Date").optional(), extra: z.string().optional(), field: z.enum(["timestamp_opt", "info_changed", "ecomm_date"]), op: z.enum(["greater", "less", "is", "not", "blank", "blank_not", "within", "notwithin"]), value: z.string()}), z.object({condition_type: z.literal("EmailClient").optional(), field: z.literal("email_client"), op: z.enum(["client_is", "client_not"]), value: z.string()}), z.object({condition_type: z.literal("Language").optional(), field: z.literal("language"), op: z.enum(["is", "not"]), value: z.string()}), z.object({condition_type: z.literal("MemberRating").optional(), field: z.literal("rating"), op: z.enum(["is", "not", "greater", "less"]), value: z.union([z.number(), z.string()])}), z.object({condition_type: z.literal("SignupSource"), field: z.literal("source"), op: z.enum(["source_is", "source_not"]), value: z.string().optional()}), z.object({condition_type: z.literal("SurveyMonkey").optional(), field: z.literal("survey_monkey"), op: z.enum(["started", "completed", "not_started", "not_completed"]), value: z.string()}), z.object({condition_type: z.literal("VIP").optional(), field: z.literal("gmonkey"), op: z.enum(["member", "notmember"])}), z.object({condition_type: z.literal("Interests").optional(), field: z.string().optional(), op: z.enum(["interestcontains", "interestcontainsall", "interestnotcontains"]).optional(), value: z.array(z.string()).optional()}), z.object({condition_type: z.literal("EcommCategory").optional(), field: z.enum(["ecomm_cat", "ecomm_prod"]).optional(), op: z.enum(["is", "not", "contains", "notcontain", "starts", "ends"]).optional(), value: z.string().optional()}), z.object({condition_type: z.literal("EcommNumber").optional(), field: z.enum(["ecomm_spent_avg", "ecomm_orders", "ecomm_prod_all", "ecomm_avg_ord"]), op: z.enum(["is", "not", "greater", "less"]), value: z.union([z.number(), z.string()])}), z.object({condition_type: z.literal("EcommPurchased").optional(), field: z.literal("ecomm_purchased").optional(), op: z.enum(["member", "notmember"]).optional()}), z.object({condition_type: z.literal("EcommSpent").optional(), field: z.enum(["ecomm_spent_one", "ecomm_spent_all"]).optional(), op: z.enum(["greater", "less"]).optional(), value: z.union([z.number(), z.string()]).optional()}), z.object({condition_type: z.literal("EcommStore").optional(), field: z.literal("ecomm_store").optional(), op: z.enum(["is", "not"]).optional(), value: z.string().optional()}), z.object({condition_type: z.literal("GoalActivity").optional(), field: z.literal("goal"), op: z.enum(["is", "goal_not", "contains", "goal_notcontain", "starts", "ends"]), value: z.string()}), z.object({condition_type: z.literal("GoalTimestamp").optional(), field: z.literal("goal_last_visited"), op: z.enum(["greater", "less", "is"]), value: z.string()}), z.object({condition_type: z.literal("FuzzySegment").optional(), field: z.literal("fuzzy_segment"), op: z.enum(["fuzzy_is", "fuzzy_not"]), value: z.union([z.number(), z.string()])}), z.object({condition_type: z.literal("StaticSegment").optional(), field: z.literal("static_segment"), op: z.enum(["static_is", "static_not"]), value: z.union([z.number(), z.string()])}), z.object({condition_type: z.literal("IPGeoCountryState").optional(), field: z.literal("ipgeo"), op: z.enum(["ipgeocountry", "ipgeonotcountry", "ipgeostate", "ipgeonotstate"]), value: z.string()}), z.object({addr: z.string(), condition_type: z.literal("IPGeoIn").optional(), field: z.literal("ipgeo"), lat: z.string(), lng: z.string(), op: z.enum(["ipgeoin", "ipgeonotin"]), value: z.number().int()}), z.object({condition_type: z.literal("IPGeoInZip").optional(), extra: z.number().int(), field: z.literal("ipgeo"), op: z.literal("ipgeoinzip"), value: z.number().int()}), z.object({condition_type: z.literal("IPGeoUnknown").optional(), field: z.literal("ipgeo"), op: z.literal("ipgeounknown")}), z.object({condition_type: z.literal("IPGeoZip").optional(), field: z.literal("ipgeo"), op: z.enum(["ipgeoiszip", "ipgeonotzip"]), value: z.number().int()}), z.object({condition_type: z.literal("SocialAge").optional(), field: z.literal("social_age"), op: z.enum(["is", "not"]), value: z.enum(["18-24", "25-34", "35-54", "55+"])}), z.object({condition_type: z.literal("SocialGender").optional(), field: z.literal("social_gender"), op: z.enum(["is", "not"]), value: z.enum(["male", "female"])}), z.object({condition_type: z.literal("SocialInfluence").optional(), field: z.literal("social_influence"), op: z.enum(["is", "not", "greater", "less"]), value: z.number()}), z.object({condition_type: z.literal("SocialNetworkMember").optional(), field: z.literal("social_network"), op: z.enum(["member", "notmember"]), value: z.enum(["twitter", "facebook", "linkedin", "flickr", "foursquare", "lastfm", "myspace", "quora", "vimeo", "yelp", "youtube"])}), z.object({condition_type: z.literal("SocialNetworkFollow").optional(), field: z.literal("social_network"), op: z.enum(["follow", "notfollow"]), value: z.literal("twitter_follow")}), z.object({condition_type: z.literal("AddressMerge").optional(), field: z.string(), op: z.enum(["contains", "notcontain", "blank", "blank_not"]), value: z.string().optional()}), z.object({condition_type: z.literal("ZipMerge").optional(), extra: z.string(), field: z.string(), op: z.literal("geoin"), value: z.string()}), z.object({condition_type: z.literal("BirthdayMerge").optional(), field: z.string(), op: z.enum(["is", "not", "blank", "blank_not"]), value: z.string().optional()}), z.object({condition_type: z.literal("DateMerge").optional(), field: z.string(), op: z.enum(["is", "not", "less", "blank", "blank_not", "greater"]), value: z.string().optional()}), z.object({condition_type: z.literal("SelectMerge").optional(), field: z.string(), op: z.enum(["is", "not", "blank", "blank_not", "notcontain", "contains"]), value: z.string().optional()}), z.object({condition_type: z.literal("TextMerge").optional(), field: z.string(), op: z.enum(["is", "not", "contains", "notcontain", "starts", "ends", "greater", "less", "blank", "blank_not"]), value: z.string().optional()}), z.object({condition_type: z.literal("EmailAddress").optional(), field: z.enum(["merge0", "EMAIL"]), op: z.enum(["is", "not", "contains", "notcontain", "starts", "ends", "greater", "less"]), value: z.string().optional()}), z.object({condition_type: z.literal("PredictedGender").optional(), field: z.literal("predicted_gender"), op: z.enum(["is", "not"]), value: z.enum(["male", "female"])}), z.object({condition_type: z.literal("PredictedAge").optional(), field: z.literal("predicted_age_range"), op: z.literal("is"), value: z.enum(["18-24", "25-34", "35-44", "45-54", "55-64", "65+"])}), z.object({condition_type: z.literal("NewSubscribers").optional(), field: z.literal("timestamp_opt").optional(), op: z.literal("date_within").optional(), value: z.string().optional()})])).optional(), match: z.enum(["any", "all"]).optional(), prebuilt_segment_id: z.string().optional(), saved_segment_id: z.union([z.number().int(), z.string()]).optional()}).optional(), segment_text: z.string().optional()}).optional(), report_summary: z.object({click_rate: z.number().optional(), clicks: z.number().int().optional(), ecommerce: z.object({total_orders: z.number().int().optional(), total_revenue: z.number().optional(), total_spent: z.number().optional()}).optional(), open_rate: z.number().optional(), opens: z.number().int().optional(), subscriber_clicks: z.number().int().optional(), unique_opens: z.number().int().optional()}).optional(), resend_shortcut_eligibility: z.object({to_new_subscribers: z.object({is_eligible: z.boolean().optional(), reason: z.string().optional()}).optional(), to_non_clickers: z.object({is_eligible: z.boolean().optional(), reason: z.string().optional()}).optional(), to_non_openers: z.object({is_eligible: z.boolean().optional(), reason: z.string().optional()}).optional(), to_non_purchasers: z.object({is_eligible: z.boolean().optional(), reason: z.string().optional()}).optional()}).optional(), resend_shortcut_usage: z.object({original_campaign: z.object({id: z.string().optional(), shortcut_type: z.enum(["non_openers", "new_subscribers", "non_clickers", "non_purchasers"]).optional(), title: z.string().optional(), web_id: z.number().int().optional()}).optional(), shortcut_campaigns: z.array(z.object({id: z.string().optional(), send_time: z.string().optional(), shortcut_type: z.enum(["non_openers", "new_subscribers", "non_clickers", "non_purchasers"]).optional(), status: z.enum(["save", "paused", "schedule", "sending", "sent", "canceled", "canceling", "archived"]).optional(), web_id: z.number().int().optional()})).optional()}).optional(), resendable: z.boolean().optional(), rss_opts: z.object({constrain_rss_img: z.boolean().optional(), feed_url: z.string().optional(), frequency: z.enum(["daily", "weekly", "monthly"]).optional(), last_sent: z.string().optional(), schedule: z.object({daily_send: z.object({friday: z.boolean().optional(), monday: z.boolean().optional(), saturday: z.boolean().optional(), sunday: z.boolean().optional(), thursday: z.boolean().optional(), tuesday: z.boolean().optional(), wednesday: z.boolean().optional()}).optional(), hour: z.number().int().gte(0).lte(23).optional(), monthly_send_date: z.number().gte(0).lte(31).optional(), weekly_send_day: z.enum(["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]).optional()}).optional()}).optional(), send_time: z.string().optional(), settings: z.object({authenticate: z.boolean().optional(), auto_fb_post: z.array(z.string()).optional(), auto_footer: z.boolean().optional(), auto_tweet: z.boolean().optional(), drag_and_drop: z.boolean().optional(), fb_comments: z.boolean().optional(), folder_id: z.string().optional(), from_name: z.string().optional(), inline_css: z.boolean().optional(), preview_text: z.string().optional(), reply_to: z.string().optional(), subject_line: z.string().optional(), template_id: z.number().int().optional(), timewarp: z.boolean().optional(), title: z.string().optional(), to_name: z.string().optional(), use_conversation: z.boolean().optional()}).optional(), social_card: z.object({description: z.string().optional(), image_url: z.string().optional(), title: z.string().optional()}).optional(), status: z.enum(["save", "paused", "schedule", "sending", "sent", "canceled", "canceling", "archived"]).optional(), tracking: z.object({capsule: z.object({notes: z.boolean().optional()}).optional(), clicktale: z.string().optional(), ecomm360: z.boolean().optional(), goal_tracking: z.boolean().optional(), google_analytics: z.string().optional(), html_clicks: z.boolean().optional(), opens: z.boolean().optional(), salesforce: z.object({campaign: z.boolean().optional(), notes: z.boolean().optional()}).optional(), text_clicks: z.boolean().optional()}).optional(), type: z.enum(["regular", "plaintext", "absplit", "rss", "variate"]).optional(), variate_settings: z.object({combinations: z.array(z.object({content_description: z.number().int().optional(), from_name: z.number().int().optional(), id: z.string().optional(), recipients: z.number().int().optional(), reply_to: z.number().int().optional(), send_time: z.number().int().optional(), subject_line: z.number().int().optional()})).optional(), contents: z.array(z.string()).optional(), from_names: z.array(z.string()).optional(), reply_to_addresses: z.array(z.string()).optional(), send_times: z.array(z.string()).optional(), subject_lines: z.array(z.string()).optional(), test_size: z.number().int().optional(), wait_time: z.number().int().optional(), winner_criteria: z.enum(["opens", "clicks", "manual", "total_revenue"]).optional(), winning_campaign_id: z.string().optional(), winning_combination_id: z.string().optional()}).optional(), web_id: z.number().int().optional()})).optional(), total_items: z.number().int().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 }; /** * List campaigns * * GET /3.0/campaigns * * Also accepts auth: oauth2 */ export const getApi30Campaigns = async (args: GetApi30CampaignsArgs, options?: GetApi30CampaignsOptions) => { const {baseUrl = "https://api.mailchimp.com", fetch: fetchFn = fetch, bearerToken} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.fields != null) query.append("fields", args.fields.join(',')) if (args.exclude_fields != null) query.append("exclude_fields", args.exclude_fields.join(',')) if (args.count != null) query.append("count", String(args.count)) if (args.offset != null) query.append("offset", String(args.offset)) if (args.type != null) query.append("type", String(args.type)) if (args.status != null) query.append("status", String(args.status)) if (args.before_send_time != null) query.append("before_send_time", String(args.before_send_time)) if (args.since_send_time != null) query.append("since_send_time", String(args.since_send_time)) if (args.before_create_time != null) query.append("before_create_time", String(args.before_create_time)) if (args.since_create_time != null) query.append("since_create_time", String(args.since_create_time)) if (args.list_id != null) query.append("list_id", String(args.list_id)) if (args.folder_id != null) query.append("folder_id", String(args.folder_id)) if (args.member_id != null) query.append("member_id", String(args.member_id)) if (args.sort_field != null) query.append("sort_field", String(args.sort_field)) if (args.sort_dir != null) query.append("sort_dir", String(args.sort_dir)) if (args.include_resend_shortcut_eligibility != null) query.append("include_resend_shortcut_eligibility", String(args.include_resend_shortcut_eligibility)) if (args.include_resend_shortcut_usage != null) query.append("include_resend_shortcut_usage", String(args.include_resend_shortcut_usage)) if (bearerToken !== undefined) headers.set('authorization', `Bearer ${bearerToken}`) const url = `${baseUrl.replace(/\/+$/, '')}/3.0/campaigns${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(getApi30CampaignsResponse, await res.json()) };