Skip to main content
Glama
newsletterSchemas.js5.16 kB
import { z } from 'zod'; import { ghostIdSchema, emailSchema, slugSchema } from './common.js'; /** * Newsletter Schemas for Ghost CMS * Provides input/output validation for newsletter operations */ // ----- Input Schemas ----- /** * Schema for creating a new newsletter * Required: name * Optional: description, sender details, design options, etc. */ export const createNewsletterSchema = z.object({ name: z.string().min(1, 'Name cannot be empty').max(191, 'Name cannot exceed 191 characters'), description: z.string().max(2000, 'Description cannot exceed 2000 characters').optional(), slug: slugSchema.optional(), sender_name: z.string().max(191, 'Sender name cannot exceed 191 characters').optional(), sender_email: emailSchema.optional(), sender_reply_to: z .enum(['newsletter', 'support'], { errorMap: () => ({ message: 'Sender reply-to must be newsletter or support' }), }) .default('newsletter'), status: z .enum(['active', 'archived'], { errorMap: () => ({ message: 'Status must be active or archived' }), }) .default('active'), visibility: z .enum(['members', 'paid'], { errorMap: () => ({ message: 'Visibility must be members or paid' }), }) .default('members'), subscribe_on_signup: z .boolean() .default(true) .describe('Whether new members are automatically subscribed'), sort_order: z .number() .int() .min(0, 'Sort order must be non-negative') .optional() .describe('Display order for newsletters'), header_image: z.string().url('Invalid header image URL').optional(), show_header_icon: z.boolean().default(true), show_header_title: z.boolean().default(true), title_font_category: z .enum(['serif', 'sans-serif'], { errorMap: () => ({ message: 'Title font category must be serif or sans-serif' }), }) .default('sans-serif'), title_alignment: z .enum(['left', 'center'], { errorMap: () => ({ message: 'Title alignment must be left or center' }), }) .default('center'), show_feature_image: z.boolean().default(true), body_font_category: z .enum(['serif', 'sans-serif'], { errorMap: () => ({ message: 'Body font category must be serif or sans-serif' }), }) .default('sans-serif'), footer_content: z.string().optional(), show_badge: z.boolean().default(true), show_header_name: z.boolean().default(true).optional(), show_post_title_section: z.boolean().default(true).optional(), }); /** * Schema for updating an existing newsletter * All fields optional */ export const updateNewsletterSchema = createNewsletterSchema.partial(); /** * Schema for newsletter query/filter parameters */ export const newsletterQuerySchema = z.object({ limit: z.number().int().min(1).max(100).default(15).optional(), page: z.number().int().min(1).default(1).optional(), filter: z .string() .regex(/^[a-zA-Z0-9_\-:.'"\s,[\]<>=!+]+$/, 'Invalid filter: contains disallowed characters') .optional() .describe('NQL filter string (e.g., "status:active")'), order: z .string() .optional() .describe('Order results (e.g., "sort_order ASC", "created_at DESC")'), }); /** * Schema for newsletter ID parameter */ export const newsletterIdSchema = z.object({ id: ghostIdSchema, }); /** * Schema for newsletter slug parameter */ export const newsletterSlugSchema = z.object({ slug: slugSchema, }); // ----- Output Schemas ----- /** * Schema for a Ghost newsletter object (as returned by the API) */ export const newsletterOutputSchema = z.object({ id: ghostIdSchema, uuid: z.string().uuid(), name: z.string(), description: z.string().nullable().optional(), slug: z.string(), sender_name: z.string().nullable().optional(), sender_email: z.string().email().nullable().optional(), sender_reply_to: z.enum(['newsletter', 'support']), status: z.enum(['active', 'archived']), visibility: z.enum(['members', 'paid']), subscribe_on_signup: z.boolean(), sort_order: z.number(), header_image: z.string().url().nullable().optional(), show_header_icon: z.boolean(), show_header_title: z.boolean(), title_font_category: z.string(), title_alignment: z.enum(['left', 'center']), show_feature_image: z.boolean(), body_font_category: z.string(), footer_content: z.string().nullable().optional(), show_badge: z.boolean(), show_header_name: z.boolean().optional(), show_post_title_section: z.boolean().optional(), created_at: z.string().datetime(), updated_at: z.string().datetime(), count: z .object({ members: z.number().optional(), posts: z.number().optional(), }) .optional(), }); /** * Schema for array of newsletters */ export const newslettersArraySchema = z.array(newsletterOutputSchema); /** * Schema for paginated newsletter response */ export const newslettersPaginatedSchema = z.object({ newsletters: newslettersArraySchema, meta: z.object({ pagination: z.object({ page: z.number(), limit: z.number(), pages: z.number(), total: z.number(), next: z.number().nullable(), prev: z.number().nullable(), }), }), });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jgardner04/Ghost-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server