@mailflix/actions
v0.0.2
Published
Declare website actions and expose Mailflix-compatible OpenAPI endpoints.
Maintainers
Readme
@mailflix/actions
Declare website actions in code and expose them to Mailflix as generated OpenAPI, a discovery manifest, and signed execution endpoints.
Install
npm install @mailflix/actionsFor pnpm or Yarn:
pnpm add @mailflix/actions
yarn add @mailflix/actionsNext.js setup
After installing the package in a Next.js App Router project, scaffold the registry file and route handlers:
npx mailflix-actions init --title "Acme Actions"The CLI creates:
mailflix-actions.tsorsrc/mailflix-actions.tsapp/.well-known/mailflix-actions.json/route.tsapp/api/mailflix/openapi.json/route.tsapp/api/mailflix/actions/[action]/route.ts
Useful options:
npx mailflix-actions init --dir ./apps/web
npx mailflix-actions init --app-dir src/app --registry src/mailflix-actions.ts
npx mailflix-actions init --secret-env MAILFLIX_ACTIONS_SECRET --forceIf you are running it before installing the package, use:
npx -p @mailflix/actions mailflix-actions initimport { createMailflixActions } from "@mailflix/actions";
export const mf = createMailflixActions({
title: "Acme Actions",
secret: process.env.MAILFLIX_ACTIONS_SECRET,
});
mf.action(
"add_to_cart",
{
title: "Add to cart",
inputSchema: {
type: "object",
properties: {
variantId: { type: "string" },
quantity: { type: "integer", minimum: 1, maximum: 10 },
},
required: ["variantId", "quantity"],
},
confirmation: "soft",
destructive: false,
},
async ({ input, visitor }) => {
return { accepted: true, input, visitor };
},
);Next.js app router
Use explicit route files when you want conventional URLs:
// app/.well-known/mailflix-actions.json/route.ts
import { mf } from "@/mailflix-actions";
export const GET = mf.discovery;// app/api/mailflix/openapi.json/route.ts
import { mf } from "@/mailflix-actions";
export const GET = mf.openapi;// app/api/mailflix/actions/[action]/route.ts
import { mf } from "@/mailflix-actions";
export const POST = mf.nextHandlers().execute.POST;Or mount one catch-all route and let the package route discovery, OpenAPI, and execution by pathname:
// app/[[...mailflix]]/route.ts
import { mf } from "@/mailflix-actions";
const handlers = mf.nextHandlers().catchAll;
export const GET = handlers.GET;
export const POST = handlers.POST;Fetch runtimes
Any Fetch-compatible runtime can use handle() directly:
export default {
fetch(request: Request) {
return mf.handle(request);
},
};Discovery
Expose discovery at /.well-known/mailflix-actions.json. The manifest points
Mailflix to the generated OpenAPI document and describes signature requirements.
When a secret is configured, mf.execute() verifies x-mailflix-timestamp and
x-mailflix-signature before running handlers.
Execution requests can also be signed manually with signMailflixBody() for
tests or non-Mailflix callers.
Niche starter kits
Starter kits register predictable small-business actions and add Mailflix
metadata to the generated OpenAPI via x-mailflix-action. Mailflix uses that
metadata for admin grouping, confirmation policy, and richer widget selection.
Only actions with handlers are registered, so you can start with the few capabilities the business actually supports.
import {
createMailflixActions,
installRestaurantKit,
} from "@mailflix/actions";
export const mf = createMailflixActions({
title: "Bistro Actions",
secret: process.env.MAILFLIX_ACTIONS_SECRET,
});
installRestaurantKit(mf, {
"restaurant.menu.search": async ({ input }) => {
return {
items: await searchMenu(input),
};
},
"restaurant.reservation.availability": async ({ input }) => {
return {
slots: await findReservationSlots(input),
};
},
});Available kits:
installRestaurantKit: menu search/details, specials, popular items, hours, reservation availability, reservation request.installRentalKit: rental search/details, pricing, availability, viewing booking, quote request.
The root import exports everything for convenience. For larger applications or bundles that prefer narrower imports, kits are also available as subpaths:
import { createMailflixActions } from "@mailflix/actions/runtime";
import { installRestaurantCatalog } from "@mailflix/actions/kits/restaurant";No API yet: expose a catalog
Small businesses can start with plain structured data. The catalog installers register handlers for search/details/pricing/availability automatically, so the same generated OpenAPI flow works even before the website has a real API.
import {
createMailflixActions,
installRestaurantCatalog,
} from "@mailflix/actions";
export const mf = createMailflixActions({
title: "Bistro Actions",
secret: process.env.MAILFLIX_ACTIONS_SECRET,
});
installRestaurantCatalog(mf, {
menuItems: [
{
id: "spicy-rigatoni",
name: "Spicy Rigatoni",
description: "Vodka sauce, basil, pecorino, Calabrian chili.",
price: 24,
currency: "$",
category: "Pasta",
allergens: ["dairy", "gluten"],
tags: ["vegetarian"],
popular: true,
},
],
reservationSlots: [
{ id: "fri-1830", date: "2026-05-08", time: "18:30", partySizeMax: 4 },
],
});import { createMailflixActions, installRentalCatalog } from "@mailflix/actions";
export const mf = createMailflixActions({
title: "Loft Rentals Actions",
secret: process.env.MAILFLIX_ACTIONS_SECRET,
});
installRentalCatalog(mf, {
items: [
{
id: "soho-loft",
name: "SoHo daylight loft",
price: 1250,
currency: "$",
period: "day",
location: "SoHo",
capacity: 40,
tags: ["events", "photo shoots"],
},
],
availabilitySlots: [
{ id: "viewing-1", rentalId: "soho-loft", date: "2026-05-08", time: "10:30" },
],
});Publishing
This package publishes compiled ESM and declarations from dist/.
pnpm --filter @mailflix/actions typecheck
pnpm --filter @mailflix/actions build
pnpm --filter @mailflix/actions publish --access publicBefore publishing, confirm the npm account belongs to the @mailflix scope:
npm whoami