Build on the supported package contract
Use @absolutejs/dispatch-postmark through its supported public entry points.
@absolutejs/dispatch-postmarkv0.1.0betaMessagingPostmark-backed EmailAdapter for @absolutejs/dispatch. Maps EmailMessage to Postmark's sendEmail params; surfaces the MessageID as DispatchResult.id.
bun add @absolutejs/dispatch-postmarkPostmark-backed EmailAdapter for @absolutejs/dispatch.
Docs: absolutejs.com/documentation/dispatch#postmark
mapMetadata
By default the adapter:
Extracts metadata.tag (string) into Postmark's Tag field
(a single-string analytics segment per message)
Maps every OTHER string-valued entry into Postmark's Metadata
(a string→string map)
Filters out non-string values (Postmark Metadata values must be
strings)
Override to customize:
Postmark's response shape:
ErrorCode !== 0 (and not undefined) → adapter throws with
Postmark ErrorCode : <Message>. @absolutejs/dispatch's error path records the exception on the dispatch.email.send span, bumps the failed counter, and emits dispatch.email.failed.
ErrorCode === 0 (or undefined) → treated as success.
DispatchResult.id = response.MessageID.
Outcomes
Use @absolutejs/dispatch-postmark through its supported public entry points.
Hardening checklist
Follow in order
Working example for Usage.
import { ServerClient } from "postmark";
import { createDispatcher } from "@absolutejs/dispatch";
import { createPostmarkAdapter } from "@absolutejs/dispatch-postmark";
const postmark = new ServerClient(process.env.POSTMARK_SERVER_TOKEN!);
const dispatcher = createDispatcher({
email: createPostmarkAdapter({
client: postmark,
defaultFrom: "no-reply@acme.io",
// Optional — defaults to Postmark's transactional stream.
// messageStream: 'broadcast',
}),
});
await dispatcher.email({
to: "alice@example.com",
subject: "Welcome to Acme",
text: "Click here to verify: ...",
// metadata.tag → Postmark Tag (single-string analytics segment);
// other string entries → Postmark Metadata (string→string map):
metadata: { tag: "onboarding", campaign: "welcome-v2" },
});Working example for API.
createPostmarkAdapter({
client, // Required — your `new ServerClient(serverToken)`
defaultFrom?, // Required if your messages don't set `from`
messageStream?, // Default: Postmark's transactional stream
mapMetadata?, // Customize EmailMessage.metadata → Postmark Tag/Metadata
})By default the adapter:
createPostmarkAdapter({
client: postmark,
mapMetadata: (metadata) => ({
Tag: typeof metadata.flow === "string" ? metadata.flow : "transactional",
Metadata: { tenant: String(metadata.tenant ?? "unknown") },
}),
});Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.
Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.
Minimal subset of Postmark's ServerClient we use. Declaring it locally keeps postmark a true peer dep — types aren't required at compile time.
type PostmarkClientLike = {
sendEmail: (params: {
From: string;
To: string;
Subject: string;
TextBody?: string;
HtmlBody?: string;
ReplyTo?: string;
Cc?: string;
Bcc?: string;
Headers?: Array<{
Name: string;
Value: string;
}>;
Tag?: string;
Metadata?: Record<string, string>;
MessageStream?: string;
}) => Promise<{
MessageID?: string;
SubmittedAt?: string;
To?: string;
ErrorCode?: number;
Message?: string;
}>;
};@absolutejs/dispatch-postmark