import { defineAction } from "../../../automation/actions" import * as z from "zod" import { requestWebflowApi, webflowAction, webflowActionSchemas, webflowInputSchema, webflowObjectSchema, webflowRequest, webflowVoidSchema, type WebflowInputValue, } from "../lib" import type { WebflowCurrentOperations } from "../current-api-types" type OperationId = keyof WebflowCurrentOperations type Query = WebflowCurrentOperations[TId]["query"] type Body = WebflowCurrentOperations[TId]["body"] type Output = WebflowCurrentOperations[TId]["output"] type SiteInput = WebflowInputValue< Query & Body > & { siteId: string } type CollectionInput = WebflowInputValue> & { collectionId: string } type CommentInput = WebflowInputValue> & { commentThreadId: string siteId: string } type FontInput = WebflowInputValue< Query & Body > & { fontId: string; siteId: string } const ID_SCHEMA = z.string().trim().min(1) const PAGINATION_SHAPE = { limit: z.number().int().min(1).max(100).optional(), offset: z.number().int().nonnegative().optional(), } const ANALYZE_DIMENSION_SHAPE = { browser: z.string().optional(), country: z.string().length(2).optional(), deviceType: z.enum(["desktop", "mobile", "tablet"]).optional(), pagePath: z.string().optional(), referrer: z.string().optional(), trafficSource: z.string().optional(), utmCampaign: z.string().optional(), utmMedium: z.string().optional(), utmSource: z.string().optional(), } const ANALYZE_EVENT_DIMENSION_SHAPE = { browser: z.string().optional(), country: z.string().length(2).optional(), deviceType: z.enum(["desktop", "mobile", "tablet"]).optional(), pagePath: z.string().optional(), trafficSource: z.string().optional(), utmCampaign: z.string().optional(), utmMedium: z.string().optional(), utmSource: z.string().optional(), } const ANALYZE_FILTER_OPERATOR_SCHEMA = z .strictObject({ eq: z.string().optional(), in: z.array(z.string()).min(1).optional(), ne: z.string().optional(), nin: z.array(z.string()).min(1).optional(), }) .refine( ({ eq, in: included, ne, nin }) => eq !== undefined || included !== undefined || ne !== undefined || nin !== undefined, { message: "Provide at least one Analyze filter operator." }, ) const ANALYZE_FILTER_KEYS = [ "audienceIds", "browser", "collectionId", "country", "dayOfWeek", "deviceBrand", "deviceType", "domain", "itemSlug", "language", "locale", "nextCollectionId", "nextItemSlug", "nextPageId", "os", "pageId", "pagePath", "previousCollectionId", "previousItemSlug", "previousPageId", "referrer", "region", "timeOfDay", "timezone", "trafficSource", "utmCampaign", "utmContent", "utmMedium", "utmSource", "utmTerm", "visitStatus", ] as const const ANALYZE_EVENT_FILTER_KEYS = [ "audienceIds", "browser", "collectionId", "country", "dayOfWeek", "deviceBrand", "deviceType", "domain", "itemSlug", "language", "locale", "os", "pageId", "pagePath", "region", "timeOfDay", "timezone", "trafficSource", "utmCampaign", "utmContent", "utmMedium", "utmSource", "utmTerm", "visitStatus", ] as const const ANALYZE_FILTER_SCHEMA = z.partialRecord( z.enum(ANALYZE_FILTER_KEYS), ANALYZE_FILTER_OPERATOR_SCHEMA, ) const ANALYZE_EVENT_FILTER_SCHEMA = z.partialRecord( z.enum(ANALYZE_EVENT_FILTER_KEYS), ANALYZE_FILTER_OPERATOR_SCHEMA, ) const ANALYZE_UTC_TIME_SCHEMA = z.string().datetime().regex(/Z$/) const ANALYZE_START_TIME_SCHEMA = ANALYZE_UTC_TIME_SCHEMA.refine( (value) => Date.parse(value) >= Date.UTC(2025, 3, 9), { message: "Analyze startTime must be on or after 2025-04-09T00:00:00Z." }, ) const ANALYZE_WINDOW_SCHEMA = z.object({ endTime: ANALYZE_UTC_TIME_SCHEMA, startTime: ANALYZE_START_TIME_SCHEMA, }) const ANALYZE_FILTER_CONFLICT_SCHEMA = z.looseObject({ filter: z.record(z.string(), z.unknown()).optional(), }) const IANA_TIME_ZONE_SCHEMA = z.string().min(1).refine(isIanaTimeZone, { message: "Expected an IANA time zone.", }) const ANALYZE_BASE_SHAPE = { ...ANALYZE_DIMENSION_SHAPE, endTime: ANALYZE_UTC_TIME_SCHEMA, filter: ANALYZE_FILTER_SCHEMA.optional(), siteId: ID_SCHEMA, startTime: ANALYZE_START_TIME_SCHEMA, } const GOOGLE_TAG_SCHEMA = z.strictObject({ displayName: z.string().min(1), order: z.number().int().nonnegative().optional(), tagId: z.string().regex(/^(?:AW|DC|G|GT)-[A-Za-z0-9]+$/), }) const CUSTOM_FONT_AXIS_SCHEMA = z .strictObject({ defaultValue: z.number(), max: z.number(), min: z.number(), name: z.string().optional(), tag: z.string().regex(/^[\x20-\x7e]{4}$/), }) .refine( ({ defaultValue, max, min }) => min <= defaultValue && defaultValue <= max, { message: "Axis defaultValue must be between min and max." }, ) const FONT_DISPLAY_SCHEMA = z.enum([ "auto", "block", "fallback", "optional", "swap", ]) const CUSTOM_FONT_AXES_SCHEMA = z.array(CUSTOM_FONT_AXIS_SCHEMA).max(16) const CUSTOM_FONT_FILE_HASH_SCHEMA = z.string().regex(/^[0-9a-f]{32}$/) const CUSTOM_FONT_FILE_NAME_SCHEMA = z .string() .min(1) .max(256) .regex(/\.(?:eot|otf|ttf|woff|woff2)$/i) const CUSTOM_FONT_FAMILY_SCHEMA = z.string().min(1).max(256) const CUSTOM_FONT_CREATE_SHAPE = { axes: CUSTOM_FONT_AXES_SCHEMA.optional(), fileHash: CUSTOM_FONT_FILE_HASH_SCHEMA, fileName: CUSTOM_FONT_FILE_NAME_SCHEMA, fontDisplay: FONT_DISPLAY_SCHEMA, fontFamily: CUSTOM_FONT_FAMILY_SCHEMA, italic: z.boolean(), weight: z.number().int().min(1).max(1000), } /** * Builds a schema for Webflow Analyze's bounded UTC reporting window. * * @param shape - Endpoint-specific Analyze fields. */ function analyzeReportSchema(shape: TShape) { return z .strictObject({ ...ANALYZE_BASE_SHAPE, ...shape }) .refine(isAnalyzeEndAfterStart, { message: "Analyze endTime must be after startTime.", }) .refine(isAnalyzeWindowAtMost100Days, { message: "Analyze reporting windows cannot exceed 100 days.", }) .refine(hasNoDuplicateAnalyzeFilters, { message: "Filter each Analyze dimension either at the top level or in filter, not both.", }) } /** * Returns whether one Analyze window ends after it starts. * * @param value - Candidate Analyze request. */ function isAnalyzeEndAfterStart(value: unknown) { const window = ANALYZE_WINDOW_SCHEMA.safeParse(value) return ( window.success && Date.parse(window.data.endTime) > Date.parse(window.data.startTime) ) } /** * Returns whether one Analyze window spans no more than 100 days. * * @param value - Candidate Analyze request. */ function isAnalyzeWindowAtMost100Days(value: unknown) { const window = ANALYZE_WINDOW_SCHEMA.safeParse(value) return ( window.success && Date.parse(window.data.endTime) - Date.parse(window.data.startTime) <= 100 * 24 * 60 * 60 * 1000 ) } /** * Returns whether no Analyze dimension is supplied through both filter forms. * * @param value - Candidate Analyze request. */ function hasNoDuplicateAnalyzeFilters(value: unknown) { const input = ANALYZE_FILTER_CONFLICT_SCHEMA.safeParse(value) if (!input.success || input.data.filter === undefined) return input.success return Object.keys(input.data.filter).every( (key) => input.data[key] === undefined, ) } /** * Returns whether a string names an IANA time zone supported by the runtime. * * @param value - Candidate time-zone name. */ function isIanaTimeZone(value: string) { try { new Intl.DateTimeFormat("en", { timeZone: value }).format(0) return true } catch { return false } } /** Lists Google tags configured for one Webflow site. */ export const listWebflowGoogleTags = webflowAction< SiteInput<"list-google-tags">, Output<"list-google-tags"> >( defineAction("List Webflow Google tags") .describe("Lists Google tags configured for one Webflow site.") .account("webflow", "sites:read") .input( webflowInputSchema>( webflowActionSchemas.id("siteId"), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/integrations/google_tags`, ), ), ) /** Adds or updates Google tags without removing unreferenced tags. */ export const upsertWebflowGoogleTags = webflowAction< SiteInput<"upsert-google-tags">, Output<"upsert-google-tags"> >( defineAction("Upsert Webflow Google tags") .describe("Adds or updates Google tags without removing other site tags.") .account("webflow", "sites:write") .input( webflowInputSchema>( z.strictObject({ googleTagIds: z.array(GOOGLE_TAG_SCHEMA).max(25), siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/integrations/google_tags`, { body: webflowRequest(input, "siteId"), method: "PATCH", }, ), ), ) /** Removes every Google tag configured for one Webflow site. */ export const deleteAllWebflowGoogleTags = webflowAction< SiteInput<"delete-google-tags">, void >( defineAction("Delete all Webflow Google tags") .describe("Removes every Google tag configured for one Webflow site.") .account("webflow", "sites:write") .input( webflowInputSchema>( webflowActionSchemas.id("siteId"), ), ) .output(webflowVoidSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/integrations/google_tags`, { method: "DELETE" }, ) }), ) /** Removes one Google tag from a Webflow site. */ export const deleteWebflowGoogleTag = webflowAction< SiteInput<"delete-google-tag"> & { tagId: string }, Output<"delete-google-tag"> >( defineAction("Delete Webflow Google tag") .describe("Removes one Google tag from a Webflow site.") .account("webflow", "sites:write") .input( webflowInputSchema & { tagId: string }>( z.strictObject({ siteId: ID_SCHEMA, tagId: GOOGLE_TAG_SCHEMA.shape.tagId, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/integrations/google_tags/${encodeURIComponent(input.tagId)}`, { method: "DELETE" }, ), ), ) /** Retrieves daily Webflow Analyze traffic metrics. */ export const getWebflowAnalyzeTrafficReport = webflowAction< SiteInput<"get-analyze-traffic-report">, Output<"get-analyze-traffic-report"> >( defineAction("Get Webflow Analyze traffic report") .describe("Retrieves a daily Webflow Analyze traffic time series.") .account("webflow", "sites:read") .input( webflowInputSchema>( analyzeReportSchema({ bucketTimeZone: IANA_TIME_ZONE_SCHEMA, metricScope: z.enum(["pageview", "session", "user"]), }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/analyze/reports/traffic`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Retrieves the most visited pages from Webflow Analyze. */ export const getWebflowAnalyzeTopPagesReport = webflowAction< SiteInput<"get-analyze-top-pages-report">, Output<"get-analyze-top-pages-report"> >( defineAction("Get Webflow Analyze top pages report") .describe("Retrieves the most visited pages from Webflow Analyze.") .account("webflow", "sites:read") .input( webflowInputSchema>( analyzeReportSchema({ limit: z.number().int().min(1).max(250).optional(), sortBy: z.enum(["pageview", "session", "user"]).optional(), timeseries: z .strictObject({ bucketTimeZone: IANA_TIME_ZONE_SCHEMA }) .optional(), }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/analyze/reports/top_pages`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Retrieves ranked dimension values from Webflow Analyze. */ export const getWebflowAnalyzeTopDimensionsReport = webflowAction< SiteInput<"get-analyze-top-dimensions-report">, Output<"get-analyze-top-dimensions-report"> >( defineAction("Get Webflow Analyze top dimensions report") .describe("Retrieves ranked dimension values from Webflow Analyze.") .account("webflow", "sites:read") .input( webflowInputSchema>( analyzeReportSchema({ dimension: z.enum([ "audienceIds", "browser", "country", "deviceType", "language", "locale", "os", "referrer", "region", "trafficSource", "utmCampaign", "utmContent", "utmMedium", "utmSource", "utmTerm", ]), limit: z.number().int().min(1).max(100).optional(), metricScope: z.enum(["session", "user"]), }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/analyze/reports/top_dimensions`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Retrieves the most frequent tracked events from Webflow Analyze. */ export const getWebflowAnalyzeTopEventsReport = webflowAction< SiteInput<"get-analyze-top-events-report">, Output<"get-analyze-top-events-report"> >( defineAction("Get Webflow Analyze top events report") .describe("Retrieves the most frequent tracked Webflow Analyze events.") .account("webflow", "sites:read") .input( webflowInputSchema>( analyzeReportSchema({ ...ANALYZE_EVENT_DIMENSION_SHAPE, filter: ANALYZE_EVENT_FILTER_SCHEMA.optional(), limit: z.number().int().min(1).max(250).optional(), timeseries: z .strictObject({ bucketTimeZone: IANA_TIME_ZONE_SCHEMA }) .optional(), }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/analyze/reports/top_events`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Retrieves time-on-page metrics from Webflow Analyze. */ export const getWebflowAnalyzeTimeOnPageReport = webflowAction< SiteInput<"get-analyze-time-on-page-report">, Output<"get-analyze-time-on-page-report"> >( defineAction("Get Webflow Analyze time on page report") .describe("Retrieves time-on-page metrics from Webflow Analyze.") .account("webflow", "sites:read") .input( webflowInputSchema>( analyzeReportSchema({ metricScope: z.enum(["pageview", "session", "user"]), timeseries: z .strictObject({ bucketTimeZone: IANA_TIME_ZONE_SCHEMA, granularityPeriod: z.enum(["day", "week"]), }) .optional(), }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/analyze/reports/time_on_page`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Updates Webflow collection metadata and field groups. */ export const updateWebflowCollection = webflowAction< CollectionInput<"patch-collection">, Output<"patch-collection"> >( defineAction("Update Webflow collection") .describe("Updates collection metadata and field groups.") .account("webflow", "cms:write") .input( webflowInputSchema>( z .strictObject({ collectionId: ID_SCHEMA, displayName: z.string().min(1).optional(), fieldGroups: z .array( z.strictObject({ description: z.string().optional(), displayName: z.string().min(1), fieldIds: z.array(ID_SCHEMA), }), ) .optional(), singularName: z.string().min(1).optional(), slug: z.string().min(1).optional(), }) .refine( ({ displayName, fieldGroups, singularName, slug }) => displayName !== undefined || fieldGroups !== undefined || singularName !== undefined || slug !== undefined, { message: "Provide at least one collection property to update." }, ), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/collections/${encodeURIComponent(input.collectionId)}`, { body: webflowRequest(input, "collectionId"), method: "PATCH", }, ), ), ) /** Resolves or reopens a Webflow design-review comment thread. */ export const resolveWebflowCommentThread = webflowAction< CommentInput<"resolve-comment-thread">, Output<"resolve-comment-thread"> >( defineAction("Resolve Webflow comment thread") .describe("Resolves or reopens a Webflow design-review comment thread.") .account("webflow", "comments:write") .input( webflowInputSchema>( z.strictObject({ commentThreadId: ID_SCHEMA, resolved: z.boolean(), siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/comments/${encodeURIComponent(input.commentThreadId)}`, { body: webflowRequest(input, "siteId", "commentThreadId"), method: "PATCH", }, ), ), ) /** Adds a reply to a Webflow design-review comment thread. */ export const createWebflowCommentReply = webflowAction< CommentInput<"create-comment-reply">, Output<"create-comment-reply"> >( defineAction("Create Webflow comment reply") .describe("Adds a reply to a Webflow design-review comment thread.") .account("webflow", "comments:write") .input( webflowInputSchema>( z.strictObject({ commentThreadId: ID_SCHEMA, content: z.string().min(1).max(2040), siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/comments/${encodeURIComponent(input.commentThreadId)}/replies`, { body: webflowRequest(input, "siteId", "commentThreadId"), method: "POST", }, ), ), ) /** Lists custom fonts registered for one Webflow site. */ export const listWebflowCustomFonts = webflowAction< SiteInput<"list-custom-fonts">, Output<"list-custom-fonts"> >( defineAction("List Webflow custom fonts") .describe("Lists custom fonts registered for one Webflow site.") .account("webflow", "sites:read") .input( webflowInputSchema>( z.strictObject({ ...PAGINATION_SHAPE, siteId: ID_SCHEMA }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts`, { query: webflowRequest(input, "siteId") }, ), ), ) /** Creates upload credentials for a custom Webflow font. */ export const createWebflowCustomFont = webflowAction< SiteInput<"create-custom-font">, Output<"create-custom-font"> >( defineAction("Create Webflow custom font") .describe("Creates upload credentials for a custom Webflow font.") .account("webflow", "sites:write") .input( webflowInputSchema>( z.strictObject({ ...CUSTOM_FONT_CREATE_SHAPE, siteId: ID_SCHEMA }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts`, { body: webflowRequest(input, "siteId"), method: "POST", }, ), ), ) /** Retrieves one custom font registered for a Webflow site. */ export const getWebflowCustomFont = webflowAction< FontInput<"get-custom-font">, Output<"get-custom-font"> >( defineAction("Get Webflow custom font") .describe("Retrieves one custom font registered for a Webflow site.") .account("webflow", "sites:read") .input( webflowInputSchema>( webflowActionSchemas.ids("siteId", "fontId"), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/${encodeURIComponent(input.fontId)}`, ), ), ) /** Updates custom-font metadata for a Webflow site. */ export const updateWebflowCustomFont = webflowAction< FontInput<"update-custom-font">, Output<"update-custom-font"> >( defineAction("Update Webflow custom font") .describe("Updates custom-font metadata for a Webflow site.") .account("webflow", "sites:write") .input( webflowInputSchema>( z .strictObject({ fontDisplay: FONT_DISPLAY_SCHEMA.optional(), fontFamily: CUSTOM_FONT_FAMILY_SCHEMA.optional(), fontId: ID_SCHEMA, italic: z.boolean().optional(), siteId: ID_SCHEMA, weight: z.number().int().min(1).max(1000).optional(), }) .refine( ({ fontDisplay, fontFamily, italic, weight }) => fontDisplay !== undefined || fontFamily !== undefined || italic !== undefined || weight !== undefined, { message: "Provide at least one font property to update." }, ), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/${encodeURIComponent(input.fontId)}`, { body: webflowRequest(input, "siteId", "fontId"), method: "PATCH", }, ), ), ) /** Deletes one custom font from a Webflow site. */ export const deleteWebflowCustomFont = webflowAction< FontInput<"delete-custom-font">, void >( defineAction("Delete Webflow custom font") .describe("Deletes one custom font from a Webflow site.") .account("webflow", "sites:write") .input( webflowInputSchema>( webflowActionSchemas.ids("siteId", "fontId"), ), ) .output(webflowVoidSchema()) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/${encodeURIComponent(input.fontId)}`, { method: "DELETE" }, ) }), ) /** Creates upload credentials for replacing a custom Webflow font file. */ export const replaceWebflowCustomFontFile = webflowAction< FontInput<"replace-custom-font-file">, Output<"replace-custom-font-file"> >( defineAction("Replace Webflow custom font file") .describe("Creates upload credentials for a replacement font file.") .account("webflow", "sites:write") .input( webflowInputSchema>( z.strictObject({ axes: CUSTOM_FONT_AXES_SCHEMA.optional(), fileHash: CUSTOM_FONT_FILE_HASH_SCHEMA, fileName: CUSTOM_FONT_FILE_NAME_SCHEMA, fontId: ID_SCHEMA, siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/${encodeURIComponent(input.fontId)}/file`, { body: webflowRequest(input, "siteId", "fontId"), method: "PUT", }, ), ), ) /** Creates upload credentials for up to 25 custom Webflow fonts. */ export const createWebflowCustomFonts = webflowAction< SiteInput<"batch-create-custom-fonts">, Output<"batch-create-custom-fonts"> >( defineAction("Create Webflow custom fonts") .describe("Creates upload credentials for up to 25 custom fonts.") .account("webflow", "sites:write") .input( webflowInputSchema>( z.strictObject({ items: z .array(z.strictObject(CUSTOM_FONT_CREATE_SHAPE)) .min(1) .max(25), siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/batchCreate`, { body: webflowRequest(input, "siteId"), method: "POST", }, ), ), ) /** Deletes up to 100 custom fonts from a Webflow site. */ export const deleteWebflowCustomFonts = webflowAction< SiteInput<"batch-delete-custom-fonts">, Output<"batch-delete-custom-fonts"> >( defineAction("Delete Webflow custom fonts") .describe("Deletes up to 100 custom fonts from a Webflow site.") .account("webflow", "sites:write") .input( webflowInputSchema>( z.strictObject({ items: z .array(z.strictObject({ id: ID_SCHEMA })) .min(1) .max(100), siteId: ID_SCHEMA, }), ), ) .output(webflowObjectSchema>()) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await requestWebflowApi( account, `/sites/${encodeURIComponent(input.siteId)}/custom_fonts/batchDelete`, { body: webflowRequest(input, "siteId"), method: "POST", }, ), ), )