Postmark integration
Ahena works with your Postmark account and one of its servers: it adds your sending domain, lists the DKIM and Return-Path records it needs and asks Postmark to re-check them, creates message streams and authenticated webhooks, and keeps Sandbox servers out of production. Your app sends straight to Postmark through the same generated email code Ahena writes for Resend. Ahena isn't in the path.
What Ahena inspects
Read-only: inspecting and Doctor never change anything at Postmark.
- Both tokens, the server and its delivery type (Live or Sandbox). Server API tokens in Postmark's responses are dropped, never returned.
- Your sending domain's DKIM and Return-Path verification, with the exact records Postmark gives.
- DMARC, through a public DNS-over-HTTPS lookup (no credentials).
- Sender signatures, and whether
email.fromis on a verified domain or a confirmed signature. - Message streams (type, archived) and webhooks (URL, stream, triggers, whether basic auth is set; never the password or header values).
- Whether your webhook route rejects a request without credentials.
What Ahena configures
You declare what you want in ahena.config.ts; ahena plan shows each change with its classification before anything is applied.
| Change | Classification | Notes |
|---|---|---|
| Add the sending domain | CONFIRMATION_REQUIRED | Publishing the DKIM TXT and Return-Path CNAME records Postmark returns is a manual DNS step; Doctor lists them. |
| Ask Postmark to re-check DKIM and Return-Path | SAFE | A DNS re-check only; changes no configuration. |
| Create a message stream | CONFIRMATION_REQUIRED | — |
| Create a webhook on a stream | CONFIRMATION_REQUIRED | Postmark doesn't sign webhooks, so Ahena sets HTTP basic auth with a generated password and stores the credentials encrypted as POSTMARK_WEBHOOK_USERNAME and POSTMARK_WEBHOOK_PASSWORD. |
| Turn on missing webhook triggers, or add basic auth to a webhook with no credentials | CONFIRMATION_REQUIRED | Triggers are only ever turned on, and credentials you set are never replaced. |
Refused outright:
- Deleting domains, streams, webhooks or servers, or archiving streams.
- Creating or changing servers; a Sandbox server can't become Live, so production needs a new Live server.
- Webhook URLs that aren't public https or that embed credentials.
Generated code: src/ahena/email/index.ts (the same email.send and email.raw interface as Resend), src/ahena/providers/postmark.ts, .env.example.postmark and, with Next.js, a webhook route that checks the basic auth credentials and rejects requests without them.
How Ahena verifies
After every write, Ahena reads Postmark back. Each change ends in one of these states, and the plan's outcome says why:
- VERIFIED: Ahena read the provider back and saw the desired state. The CLI shows ✓ only for VERIFIED.
- APPLIED_UNVERIFIED: The provider accepted the change, but the re-read doesn't show it yet (after bounded polling), or the re-read failed.
Creates are read, matched (webhooks by URL and stream) and made only if absent, then read back, with full pagination of domains and sender signatures.
Webhooks are verified by existence: URL, stream, triggers and that basic auth is set are read back; the password is never compared or returned.
Domain verification honestly stays APPLIED_UNVERIFIED while DNS propagates.
If a request may have reached Postmark but the answer was lost, Ahena re-reads before deciding: done, safe to retry, or “check before retrying”.
What Doctor diagnoses
Key checks from ahena doctor. Each finding says why it matters, where, the impact and whether Ahena can fix it. The full list is in the Postmark docs.
| Check | What it means |
|---|---|
postmark.account / postmark.server | Each token works; which one was rejected if not. |
postmark.server.delivery | A Sandbox server in production fails, with the manual steps to move to a Live server. |
postmark.domain.dkim / .return_path | Each record's state, with the exact record to add (FAIL in production). |
postmark.sender | email.from is on a DKIM-verified domain or a confirmed sender signature. |
postmark.stream | The configured stream exists and isn't archived. |
postmark.webhook / .auth | A webhook for the endpoint on the stream, with the triggers and basic auth. |
postmark.webhook.endpoint | One POST without credentials to your route: rejected is good; a 2xx means it doesn't check basic auth. |
More on findings, health and continuous checks: Doctor.
Approvals
Postmark changes here are classified CONFIRMATION_REQUIRED and SAFE. Every change is planned and shown as a diff first. Ahena itself enforces who can approve it:
- SAFE changes are applied without asking.
- In production, every other change needs approval in the Ahena dashboard, signed in, by an admin.
- In other environments, CONFIRMATION_REQUIRED changes are approved in the CLI; BILLABLE and DESTRUCTIVE changes always need the dashboard.
- MANUAL steps are never applied by Ahena.
--yes and --allow-billable don't replace a dashboard approval. See approvals for how it works and why the browser.
Manual steps
These are MANUAL: Ahena never does them. It lists them with the exact steps when they apply.
- Publish the DKIM TXT and Return-Path CNAME records Doctor lists, then run
ahena configure postmarkto have them re-checked. - Production on a Sandbox server: create a Live server in Postmark and reconnect with its Server API token.
- After deploying the webhook route with its credentials, send a test from the webhook's page in Postmark.
Credentials and permissions
| Name | Secret | Notes |
|---|---|---|
POSTMARK_ACCOUNT_TOKEN | Yes | The Account API token (account owners and admins): domains, DKIM, Return-Path and sender signatures. |
POSTMARK_SERVER_TOKEN | Yes | A Server API token for the one server this environment sends through: message streams and webhooks. |
POSTMARK_SERVER_ID | No | Optional. The server the token belongs to; Ahena checks they match. |
POSTMARK_WEBHOOK_USERNAME / POSTMARK_WEBHOOK_PASSWORD | Yes | Created by Ahena with the webhook. Read them back with ahena env reveal <environment> POSTMARK_WEBHOOK_PASSWORD (always audited). |
Least privilege
- Postmark tokens have no scopes, so Ahena checks only that each token works, and says so.
- Generate separate tokens for Ahena (a server and the account can each have up to three), and give your app only its server's token.
Each credential Ahena stores gets its own key and is envelope-encrypted, bound to the environment. See security.
Workflow example
Connect, check, plan, then apply. Placeholders (…) stand for your own values.
POSTMARK_ACCOUNT_TOKEN=… POSTMARK_SERVER_TOKEN=… ahena connect postmark -e productionConnect with both tokens.
ahena doctor -e productionCheck the server, domain, DKIM, Return-Path, DMARC, stream and webhook.
ahena plan -e productionSee the domain, stream and webhook changes.
ahena apply -e productionApprove and apply; the webhook credentials are stored encrypted.
ahena generate postmark -e production --framework nextjsWrite the email adapter and the webhook route.
Verification status
Available in beta. Every capability is validated by Ahena's automated provider contract and conformance tests; verification against the real service is next.
How Postmark is tested
- Postmark is tested against a simulated version of its API: both token types, ErrorCode errors, count/offset pagination, the read-back after each write and idempotent re-apply.
- It hasn't been run against the real Postmark API yet. Whether Postmark's webhook test deliveries carry the basic auth credentials is taken from the docs, not observed.
- The generated webhook route is typechecked against the real
postmarkSDK and run in tests; the email adapter is typechecked.
The providers overview explains Ahena's testing methodology and what has been verified for every provider.
Limitations
- Ahena doesn't delete or archive anything, rotate DKIM keys, or create servers.
ahena configure cloudflare --records-fromreads only Resend today; Postmark's DNS records are published by hand.- Account approval, suppression lists, templates and inbound processing are out of scope.
Disconnecting
ahena disconnect postmark removes the stored tokens; Postmark has no revoke API, so delete Ahena's tokens in Postmark. Servers, domains, streams and webhooks are untouched.