Postmark API Observations
Design-intent note capturing observed Postmark API behaviour relevant to Arda. This is a curated reference — not an exhaustive API catalogue. For the full Postmark API surface, see the Postmark Developer Docs.
Items are annotated with their evidence source:
from-docs— pulled from Postmark’s published documentation.observed— empirically confirmed against the live API during implementation or operator runs.
Purpose
Section titled “Purpose”This note records the Postmark API surface assumptions Arda commits to during Phase 1 and beyond. It serves two audiences:
- Operators provisioning or maintaining Arda’s Postmark accounts — consult the authentication and endpoint sections when using the Postmark API directly.
- Engineers building the
postmarkAccountProxycomponent in a later phase — use this note as the design starting point. Items markedobservedare higher-confidence thanfrom-docsitems; both are useful, butobservedentries should drive design decisions where they diverge.
The scope is limited to the API surface Arda actually touches: account-level administration (servers, domains, sender signatures) and outbound message submission. Inbound email, broadcast campaigns, and suppression management are out of scope.
Authentication
Section titled “Authentication”Token types and headers
Section titled “Token types and headers”Postmark uses two distinct token types with separate request headers:
| Token type | Header | Scope |
|---|---|---|
| Account-level token | X-Postmark-Account-Token: <token> | Operations spanning servers: GET /servers, POST /servers, POST /domains, PUT /domains/<id>/verifyDkim, etc. |
| Server-level token | X-Postmark-Server-Token: <token> | Operations within a single server: POST /email, GET /messages/outbound, etc. |
from-docs — header names are documented in the Postmark Developer Docs.
How Arda uses each token type
Section titled “How Arda uses each token type”Phase 1 provisions Postmark accounts and stores account-level tokens in 1Password (Arda-SystemsOAM vault). These tokens authenticate all account-administration API calls made by the drift-detection workflow and, in a later phase, the postmarkAccountProxy.
Server-level tokens are generated at server-creation time and stored separately (one item per Postmark server). They are consumed at runtime by the email-sending component inside each partition, not by account-management code.
Webhook authentication
Section titled “Webhook authentication”Postmark does not sign webhook payloads. Arda authenticates inbound webhook deliveries via an Authorization: Bearer <token> header configured on the webhook’s properties through Postmark’s modern Webhooks API (per DQ-011). The Bearer token is the existing ARDA_API_KEY value the Application Runtime already accepts; Arda’s webhook handler validates the header before processing any event. The token is stored in Secrets Manager and projected to pods via the External Secrets Operator (ESO) pattern — not via a GitHub Actions secret.
from-docs — see Postmark’s Webhooks API reference for the HttpHeaders property used to attach the Bearer token.
Account and Server Topology
Section titled “Account and Server Topology”Account isolation
Section titled “Account isolation”Arda operates two Postmark accounts:
- PostmarkProd — bound to production partitions (
prod,demo). Account-level credential stored in 1Password underArda-SystemsOAM/Postmark-Prod(the global-utility item; see below for per-partition copies). - PostmarkNonProd — bound to development and staging partitions (
dev,stage). Account-level credential stored in 1Password underArda-SystemsOAM/Postmark-NonProd(global-utility item).
Account-level isolation provides a hard boundary: a token for one account cannot access servers, domains, or message logs of the other. This is the primary mechanism ensuring production email traffic is never affected by development activity.
Per-partition vault copies
Section titled “Per-partition vault copies”Following the Arda partition-vault convention (Arda-{Dev,Stage,Demo,Prod}OAM), each partition also holds its own independently stored copy of the relevant Postmark account token under the service-name-only item title Postmark (the vault name carries the environment; the item title stays short):
| Partition | Vault | Item | Postmark account |
|---|---|---|---|
prod | Arda-ProdOAM | Postmark | PostmarkProd |
demo | Arda-DemoOAM | Postmark | PostmarkProd |
stage | Arda-StageOAM | Postmark | PostmarkNonProd |
dev | Arda-DevOAM | Postmark | PostmarkNonProd |
These per-partition copies are stored independently from day one so any partition can rotate or diverge without infrastructure change. They are the runtime credential source for Phase 4’s per-partition deploy tooling (postmarkCredentialOpReference(partition) in platform/postmark-service.ts — a Phase 4 prerequisite). The Arda-SystemsOAM qualified items (Postmark-Prod, Postmark-NonProd) remain for cross-partition utility use (CI drift-detection, operator tooling that spans partitions) because Arda-SystemsOAM holds both accounts and needs the qualified names for disambiguation.
Servers within an account
Section titled “Servers within an account”A Postmark server is the unit of sender identity inside an account. Each server has:
- A unique numeric
IDand a displayName. - One or more
ApiTokens(server-level). Postmark supports multiple tokens per server to enable zero-downtime rotation; Arda currently provisions one token per server. - A
DeliveryTypeofLive(production sending) orSandbox(no real delivery). Integration test runs useSandbox.
DeliveryType is the signal for live-sending readiness. Postmark’s API has no account-level sandbox endpoint: GET / 302-redirects to the marketing site, and GET /account returns HTTP 404. The correct way to determine whether a server will actually deliver mail is to read the DeliveryType field returned by GET /servers/{id} after server creation. The Phase 3 Corporate CLI checks DeliveryType after creating the Free Kanban Tool server (Phase A, step after server creation) and emits a structured log event: info level for Live, warn for Sandbox or unknown. REQ-OPS-002’s operator-confirmation intent is preserved; the implementation uses the per-server endpoint rather than a non-existent account endpoint.
A Postmark domain belongs to the account, not to a specific server. Multiple servers in the same account can send From addresses on the same domain. Arda creates both the server and the domain within the same account; no explicit server-to-domain association is required.
from-docs (server and domain structure). observed (no account-level sandbox endpoint; DeliveryType from GET /servers/{id} is the live-delivery signal).
Key Endpoints
Section titled “Key Endpoints”These are the endpoints Arda’s automation touches. Refer to the Postmark Developer Docs for full request and response schemas.
Server management
Section titled “Server management”| Method | Path | Purpose |
|---|---|---|
GET | /servers | List all servers in the account. Used for idempotent pre-flight check before creating a server. Both count and offset query parameters are required — omitting either returns HTTP 422 with ErrorCode: 600, "Parameter '<name>' is required but has been left out". The minimal probing request is GET /servers?count=1&offset=0. |
GET | /servers/<id> | Retrieve a single server by numeric ID. |
POST | /servers | Create a new server. Returns the server object including ApiTokens. |
Domain and DKIM management
Section titled “Domain and DKIM management”| Method | Path | Purpose |
|---|---|---|
GET | /domains | List all domains in the account. Used for idempotent pre-flight check. |
POST | /domains | Create a new sending domain. Returns DKIMHost and DKIMTextValue (the DNS record to publish). |
PUT | /domains/<id>/verifyDkim | Re-check DNS and update DKIMVerified. Idempotent; safe to poll. |
PUT | /domains/<id>/verifyReturnPath | Re-check the Return-Path CNAME and update ReturnPathDomainVerified. Idempotent; safe to poll. |
Key Domain response fields Arda reads:
DKIMHost— FQDN for the DKIM TXT record (e.g.,pm._domainkey.sending.example.com). The DKIM selector is derived by stripping._domainkey.<domain>.DKIMTextValue— the TXT record body. Public DNS material (it is published in DNS for receivers to fetch). Captured intocdk.context.jsonby the Phase 3 Corporate CLI’s Phase A so the CDK Stack in Phase B can emit the DNS record. Not a secret. The Postmark server token returned alongside this response is the secret; it is written directly to 1Password (e.g.,Free-Kanban-Generator-Postmark-Serverin theArda-CorporateOAMvault perDQ-R1-007) and never traverses CDK context.ReturnPathDomain/ReturnPathDomainCNAMEValue— FQDN and CNAME target for the Return-Path record. Public DNS material, same disposition as DKIM above.
Sender signatures
Section titled “Sender signatures”Sender signatures are distinct from domains: they authorize specific From addresses (individual mailboxes) rather than entire domains. Arda uses domain-level verification as the primary sending authorization; sender signatures are used only where a specific named mailbox (no-reply@…) requires explicit verification.
| Method | Path | Purpose |
|---|---|---|
GET | /senders | List sender signatures. Pre-flight check. |
POST | /senders | Create a sender signature for a specific From address. |
Outbound messages
Section titled “Outbound messages”| Method | Path | Purpose |
|---|---|---|
POST | /email | Send a single transactional message. Requires a server-level token. |
POST | /email/batch | Send up to 500 messages in one request. |
GET | /messages/outbound | Query sent message history (search by recipient, tag, status). |
Idempotency, Errors, and Rate Limits
Section titled “Idempotency, Errors, and Rate Limits”Idempotency conventions
Section titled “Idempotency conventions”Postmark does not guarantee idempotent behavior on POST resource-creation endpoints. Arda’s strategy is client-side pre-flight checking:
- Issue a
GETlist call before anyPOSTcreate call. - If a resource with the matching name already exists, skip the create and reuse the existing ID.
- Log a warning noting the resource was found pre-existing.
This pattern applies to server creation (POST /servers) and domain creation (POST /domains). The verification endpoints (verifyDkim, verifyReturnPath) are inherently idempotent on Postmark’s side — each call re-checks DNS and updates the verification flag. Arda polls them (default 5 attempts, 60 seconds apart) until both verifications succeed or the budget is exhausted.
from-docs for the documented behavior. observed annotations to be added when the drift-detection workflow runs against PostmarkProd.
Error model
Section titled “Error model”Postmark expresses errors as a JSON body alongside a standard HTTP status code:
{ "ErrorCode": 504, "Message": "Domain name 'sending.example.com' is invalid"}Arda’s client preserves both fields in a structured error type. The retry policy by HTTP status:
| HTTP status | Retryable? | Handling |
|---|---|---|
5xx | Yes | Bounded exponential backoff — 3 attempts by default, doubling the delay between each. |
422 | No | Business error (invalid name, duplicate, missing field). Surface ErrorCode + Message to the caller; do not retry. |
401 / 403 | No | Authentication failure — almost always a wrong token. Check 1Password for the correct account credential. |
429 | Deferred | Rate limit. Not observed in practice given Arda’s low request volume (~10 calls per orchestration run). If encountered, add observed header details here. |
from-docs. Unit tests cover the 5xx-retry-success, 5xx-budget-exhausted, 4xx-fail-fast, and malformed-JSON paths.
Rate limits
Section titled “Rate limits”Postmark documents X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset response headers but does not publish a specific budget for account-level operations on the Platform plan. Phase 1’s request volume is negligible. If later phases introduce higher-volume account-level traffic (e.g., bulk domain listing), capture header observations here.
from-docs.
Version-Pin Assumptions
Section titled “Version-Pin Assumptions”| Field | Value |
|---|---|
| Postmark API base URL | https://api.postmarkapp.com |
| Postmark plan | Platform |
| Observation note authored | 2026-04-28 |
| Freshness recommendation | Re-validate on the first drift-test failure attributable to surface drift, with an annual review as a backstop (DQ-R1-005). |
The platform/postmark-service.ts file in the infrastructure repository holds a structured constant (POSTMARK_API_VERSION_PIN) that records this note’s URL and the freshness date, making the version-pin machine-discoverable without duplicating content.
Cross-Links
Section titled “Cross-Links”- Postmark Service Overview — account topology, credential storage, and OAM model.
- Operator Runbook — step-by-step provisioning instructions and troubleshooting guide.
- OAM Overview — FCAPS context for the Postmark service within the broader system management model.
Copyright: © Arda Systems 2025-2026, All rights reserved