Hook Reference

VeloCMS plugins subscribe to lifecycle hooks by declaring them in the manifest and exporting a handler function with a matching name. Handlers receive a typed payload and a context object. The return value replaces the payload for subsequent handlers.

Handler signature

type PluginHandler<T> = (
  payload: T,
  context: PluginContext
) => T | Promise<T>;

All hooks

HookPayload typeFires whenModifiable
afterPostCreatePostHookPayloadAfter a new post is saved (draft or scheduled)No
afterPostUpdatePostHookPayloadAfter any post field is updatedNo
afterPostDeletePostHookPayloadAfter a post is permanently deletedNo
beforePostPublishBeforePostPublishPayloadJust before post status changes to publishedYes
afterPostPublishPostHookPayloadAfter post is visible to readersNo
afterMemberSignupMemberHookPayloadAfter a new reader registersNo
afterMemberUpdateMemberHookPayloadAfter member profile or tier is updatedNo
beforeMemberDeleteMemberHookPayloadJust before a member account is deletedNo
afterPageCreatePageHookPayloadAfter a page builder page is createdNo
afterPageUpdatePageHookPayloadAfter a page is updatedNo
afterPagePublishPageHookPayloadAfter a page is publishedNo
afterOrderCreateOrderHookPayloadAfter a new commerce order is createdNo
afterProductCreateProductHookPayloadAfter a product is createdNo
onAppStart{ timestamp: string }Once on application startupNo
onDailySchedule{ date: string }Once per day at 00:00 UTCNo

"Modifiable" = return value is used by VeloCMS. For non-modifiable hooks, the return value is ignored; return the original payload unchanged.

Declaring hooks in the manifest

{
  "hooks": ["afterMemberSignup", "onDailySchedule"]
}

PluginContext

interface PluginContext {
  tenantId: string;
  pluginId: string;
  log: (message: string, data?: Record<string, unknown>) => void;
  warn: (message: string, data?: Record<string, unknown>) => void;
  error: (message: string, data?: Record<string, unknown>) => void;
}

Payload types

interface PostHookPayload {
  post: {
    id: string;
    title: string;
    slug: string;
    content_json: Record<string, unknown> | null;
    content_html: string | null;
    content_markdown: string | null;
    excerpt: string | null;
    status: "draft" | "published" | "scheduled";
    visibility: "public" | "members_only" | "paid";
    reading_time_minutes: number | null;
    published_at: string | null; // ISO 8601
    tags: string[];
    tenant_id: string;
  };
}

interface BeforePostPublishPayload extends PostHookPayload {
  suggestedTags?: string[];
  seoSuggestions?: { title?: string; description?: string };
}

interface MemberHookPayload {
  member: {
    id: string;
    email: string;
    display_name: string | null;
    tier: "free" | "paid";
    subscribed_at: string | null;
    tenant_id: string;
  };
}