Send transactional email and manage Postmark servers, domains, and message search with dry-run mutations.
- Other
- postmark
- transactional
- servers
- domains
- messages
- search
- dry-run
- License
- MIT
- Published
- August 22, 2026
- Pinned commit
d6b8701- Rating
- No ratings yet
- Forks
- 0
- Stars
- 0
- Adaptation effort
- —
README
@kody/postmark
Intent
Reusable Postmark helpers so Kody agents can send transactional email, inspect
the current server, manage account servers and sending domains, and search
outbound or inbound messages on the caller's Postmark account — not a
shared platform inbox. Auth is a saved Server API token by default. Reads are
free-form; mutations support dryRun: true, and sends or deletes also require
confirm: true.
This listing is meant to be forked. After you fork, save your own postmark
credential and call the helpers in your account. There are no baked-in server
tokens or from-addresses.
Agent setup
Default auth is a Postmark Server API token (secret-backed). There is no built-in platform OAuth app. Do not paste the token into chat.
- Open a server at account.postmarkapp.com/servers and copy the Server API token from its API Tokens tab. Sending also needs a confirmed sender signature or verified domain on that server.
- Save it in Kody:
- In the account secrets UI, approve host
api.postmarkapp.com. Saving the secret does not approve hosts by itself.
Optional Account API token
Listing every server, creating/deleting servers, and managing domains use
X-Postmark-Account-Token. Copy that token from the account API Tokens tab
(Account Owner / Admin only):
GET /server, sending, and message search stay on the Server token.
Multi-account
There are no hard-coded account aliases:
account: "work"reads secretspostmark-workandpostmark-account-work- or pass
secretName: "postmark-work"/accountSecretName: "postmark-account-work"
Hosts, 401, and 422
| Status | Meaning | Next step |
|---|---|---|
| 401 | Missing, invalid, or wrong token type | Save postmark (or postmark-account for /servers and /domains) at the secrets URL |
| 422 + sender | from is not a confirmed sender | Confirm a sender signature or verify the domain, then pass from at call time |
| 422 + stream | messageStream does not exist on this server | Omit it (defaults to outbound) or pass a stream id from the server |
| 429 | Rate limit | Back off; batch with sendBatch when sending many messages |
Approve api.postmarkapp.com on the secret. Host approval lives in the
account security UI.
Mutation safety
- Pass
dryRun: trueon any mutation to return{ dryRun: true, method, path, body, tokenKind }without contacting Postmark. sendEmail,sendBatch,sendTemplateEmail,deleteServer,deleteDomain, androtateDkimalso throw unlessconfirm: true.- This package never stores a default
from. Pass the confirmed sender at call time. - Server payloads strip
ApiTokensso tokens cannot leak through helpers.
import { sendEmail } from 'kody:@kody/postmark/emails'
const preview = await sendEmail({
from: 'Name <hello@yourdomain.com>',
to: 'person@example.com',
subject: 'Hello from Kody',
htmlBody: '<p>It works!</p>',
dryRun: true,
})
const sent = await sendEmail({
from: 'Name <hello@yourdomain.com>',
to: 'person@example.com',
subject: 'Hello from Kody',
htmlBody: '<p>It works!</p>',
confirm: true,
})Exports
kody:@kody/postmark— action dispatcher (defaults tosmoke-test); also re-exports every helper belowkody:@kody/postmark/emails— send, batch, and template sendkody:@kody/postmark/servers— current server (Server token) plus account server list/create/edit/deletekody:@kody/postmark/domains— list, create, verify DKIM / Return-Path, deletekody:@kody/postmark/messages— outbound and inbound search plus detailskody:@kody/postmark/smoke-test— dry-run self-check, then a live server and recent-message read when credentials existkody:@kody/postmark/scheduled-server-health— no-argument read-only wrapper for the disabledserver-healthjob
Low-level transport (postmarkRequest, postmarkCall, PostmarkApiError) stays
pinned to https://api.postmarkapp.com. postmarkCall writes default to dry-run
until you pass dryRun: false.
The package-owned server-health job starts with "enabled": false.
Smoke test
import { smokeTest } from 'kody:@kody/postmark'
export default async function main() {
return await smokeTest()
}Without a postmark secret this still returns { ok: true, live: false } plus
the setup URLs. With a Server API token it reads the current server and one page
of recent outbound messages — no writes and no live email.
Share https://kody.codes/@kody/postmark — never a /community/{listing_id} URL.
Import the official package as kody:@kody/postmark. Do not invoke a personal
postmark kody id; that can resolve a different account package.
Example
import postmark from 'kody:@kody/postmark'
export default async function main() {
return await postmark({ action: 'get-server' })
}Branding
community-icon.svg uses Postmark's official stamp from
postmarkapp.com/images/logo-stamp-simple.svg
(yellow #FFDE00 scalloped mark and letter). The paths are unmodified.
Postmark® is a trademark of ActiveCampaign, LLC. This package is not affiliated
with or endorsed by Postmark or ActiveCampaign.
Docs
Report this listing
Log in to report this listing.