Guides
Write a Plugin
Add reusable cross-cutting behavior without making the core provider-aware.
Use plain middleware for behavior that only wraps the normalized send pipeline.
import type { Middleware } from "@postbote/core";
export function audit(): Middleware {
return async (ctx, next) => {
const result = await next();
await writeAuditRecord({ messageId: result.messageId, provider: ctx.adapter.name });
return result;
};
}Middleware receives a mutable SendContext and next(). It may change ctx.message or ctx.adapter, but should preserve the caller's final result semantics. A middleware that runs inside failover can be invoked multiple times, so make it idempotent.
Use a PluginObject when you need an input extension or a full send wrapper. Input transforms must return regular EmailMessageInput; extension-only fields must be removed before normalization. Only one plugin may define wrapSend.
Test plugin behavior with @postbote/testing and test adapters before publishing it.