Position List - Postmark Integration

Postmark Integration

This page describes how Postmark (a transactional email service) is used in the Position List application. It is split into two parts:

  1. Business perspective — what users can do and why Postmark is involved.

  2. Technical perspective — how the integration is wired in code.


About Postmark

What is Postmark?

Postmark is a cloud-based SaaS email service provider designed specifically for delivering transactional emails, offering both an SMTP service and a REST API. Founded in 2009, it was acquired by ActiveCampaign in 2022, though it continues to operate as a standalone product focused on transactional email delivery.

Core focus: transactional email

Transactional emails are one-to-one unique messages that the recipient is expecting to receive — usually triggered by a user action and not requiring an unsubscribe link. Common examples include welcome emails, password resets, receipts, and notifications.

Key features

Security

Postmark uses TLS encryption for email transmissions and provides guided setup for SPF, DKIM, and DMARC to protect sender reputation and reduce phishing risk.

Sender Signatures

A Sender Signature is a verification of an individual sender email address, used as an alternative to full domain verification. Before a sender can send email through Postmark, their address must be confirmed via a verification link. This is the mechanism the Position List app uses to validate each user before allowing them to send.

Integration methods

Postmark supports two integration methods:

  • REST API — via https://api.postmarkapp.com, authenticated with server or account tokens passed as HTTP headers.

  • SMTP — a standard SMTP endpoint for apps that prefer that approach.

Official and community SDKs are available for most major languages (.NET, Ruby, Node.js, Python, etc.). Note that the Position List integration uses a custom HttpClient wrapper rather than the official SDK.


Part 1 — Business Perspective

Why Postmark?

The application lets users send exported position lists by email directly from the platform. Postmark is the underlying delivery service that actually sends those emails and reports back on delivery status (delivered, bounced, opened, clicked).

Using Postmark gives the business:

  • A trusted, monitored delivery channel for outbound mail.

  • Per-recipient delivery insight (was it received? opened? clicked?).

  • Verified sender identities so messages are less likely to be flagged as spam.

What Users Can Do

1. Send a position list by email

A user can compose an email (recipients, CC/BCC, subject, rich-text body) with an exported position list embedded, and send it. The email is delivered by Postmark on behalf of the user's verified address.

2. Verify the sender identity (one-time setup)

Before a user can send, their email address must be confirmed as a Postmark Sender Signature:

  1. The first time a user tries to send, the app asks Postmark to register their address as a sender.

  2. Postmark emails the user a confirmation link.

  3. Once the user clicks it, sending is enabled.

If verification has not happened yet, the composer shows an overlay explaining the situation and offers a Re-send verification email action.

3. View email history & delivery events

For any email sent through the app, the user can open a details view that shows:

  • Subject, recipients, and the rendered HTML body.

  • A per-recipient timeline of events: Delivered, Bounced, Opened, Clicked, etc., each with timestamps.

Note: Postmark retains message details for 45 days. Older emails will still show the metadata stored in our database, but the live event timeline will not be available.

4. Manage sender signatures (admin)

Administrators can manage the list of registered sender signatures across the organization, view their confirmation status, and trigger re-sends of verification emails.

5. Distribution lists

Users can save groups of recipients as named distribution lists and reuse them when composing emails. The app expands list names into individual addresses before handing the message to Postmark.

End-to-End User Journey

Step

Unverified path

Verified path

1

Compose email

Compose email

2

App detects signature not confirmed → show Postmark overlay

App confirms signature → send via Postmark

3

User triggers re-send of verification email

Store MessageID + history in DB

4

User views per-recipient delivery events

Operational Notes

  • Two Postmark credentials are used: one for managing senders, one for sending email. Both live in secure configuration; rotation is an ops task.

  • Templates are defined inside Postmark itself (not in the repo). The app references them by numeric template ID.

  • Cost / volume scales with the number of emails sent, since every send is one Postmark API call.


Part 2 — Technical Perspective

High-Level Architecture

React UI  ──►  MVC Controllers  ──►  PostmarkHelper  ──►  Postmark REST API
                     │
                     ▼
                 Database
        (sender signatures, sent-email history with Postmark MessageID)

The frontend never talks to Postmark directly. All Postmark calls go through two MVC controllers, which delegate to a single helper class.

1. Configuration & Startup

Registered in Infrastructure/Extensions/StartupExtensions.cs:121:

C#
builder.Services.AddScoped<PostmarkHelper>();

Two secrets are read from configuration:

Key

Used for

secrets:PostMarkAccountTokenValue

Sender-signature endpoints (/senders/*)

secrets:PostMarkServerTokenValue

Email-sending and message-detail endpoints (/email/*, /messages/*)

In dev, these come from secure.config at C:\WebDocs\AppData\PL.env (StartupExtensions.cs:40-41).

2. Server-Side Helper

Common/Helper/PostmarkHelper.cs is a custom HttpClient wrapper — the official Postmark SDK is not used. Base URL: https://api.postmarkapp.com.

Method

Postmark Endpoint

Purpose

CreateSenderSignature(email, name)

POST /senders

Create a verified sender

GetSenderSignature(id)

GET /senders/{id}

Read signature / verification status

ResendSignatureEmail(id)

POST /senders/{id}/resend

Resend verification email

SendEmailWithOwnerPositionTemplate(...)

POST /email/withTemplate (template 16534707)

Send position list (main template)

SendEmailWithEmailTemplateOwnerPositionTemplate(...)

POST /email/withTemplate (template 23095352)

Send via custom template

GetMessageDetails(messageGuid)

GET /messages/outbound/{guid}/details

Fetch delivery events

Auth headers:

  • X-Postmark-Account-Token — for /senders/*

  • X-Postmark-Server-Token — for /email/* and /messages/*

Returns an OperationResult wrapper with Success and Result (raw JSON). Failures are logged via Serilog (PostmarkHelper.cs:178).

3. DTOs

  • PostmarkEmailTemplate.csTemplateId/TemplateAlias, TemplateModel, From/To/Cc/Bcc/ReplyTo, TrackOpens/TrackLinks, Headers, Attachments.

  • PostmarkSenderSignature.csEmailAddress, Name, ID, Confirmed, Domain.

  • PostmarkMessageDetails.csHtmlBody, Subject, From, Status, recipients, and MessageEvents (delivery, bounce, open, click with timestamps).

4. Business Layer

  • Business/PositionList/SenderSignatureBusiness.cs — DB-side CRUD for signatures (uspGetSendersSignatures), current-user signature, and HTML email-signature management. Does not call Postmark directly.

  • Business/PositionList/ExportFormatBusiness.csPersistSentEmailWithExportedList() saves the Postmark MessageID so the UI can later fetch delivery events.

5. MVC Controllers (Frontend-Facing API)

SendersSignaturesController.cs

Route

Purpose

POST /Positions/SendersSignatures/GetList

List signatures (admin)

POST /Positions/SendersSignatures/GetSignatureDetails

Single signature details

POST /Positions/SendersSignatures/SaveSignatureDetails

Create/update; calls CreateSenderSignature if new

POST /Positions/SendersSignatures/SendPostmarkConfirmationEmail

Resend verification (or create if missing)

POST /Positions/SendersSignatures/GetEmailSignature / ModifyEmailSignature

User HTML email signature

ExportFormatsController.cs

Route

Purpose

GET /Positions/ExportFormats/IsPostmarkActive (line 432)

Confirms user signature; auto-creates if missing

POST /Positions/ExportFormats/SendEmail (line 462)

Sends an email + persists history

POST /Positions/ExportFormats/ResendSignatureEmail (line 495)

Trigger resend of verification

POST /Positions/ExportFormats/GetEmailedExportedListDetails (line 184)

Fetch sent email + Postmark events

6. Frontend

React: email-export-editor

Component

Role

EmailExportEditorsContainer.tsx

Top-level orchestrator; exposes startEditor, insertListToEditor, startWithTemplate for the legacy bridge

EmailEditorPanel.tsx

Single floating composer (To/Cc/Bcc/Subject + TinyMCE body); calls sendEmail(). Shows PostmarkOverlay when signature unconfirmed; "Send" becomes "Register to send"

EmailHistoryDetailsModal.tsx

Renders Postmark event timeline grouped by recipient; falls back when email is older than Postmark's 45-day retention

PostmarkOverlay.tsx

Explains the verification step and triggers resendSignatureEmail()

API Client: api.ts

Functions: isPostmarkActive(), sendEmail(), resendSignatureEmail(), getEmailHistoryDetails(), getEmailSignature(), saveEmailSignature().

Hooks

  • useSharedData.ts — boots isPostmarkActive, distribution lists, and signature; listens for legacy jQuery PubSub events.

  • useEmailEditors.ts — manages up to 3 floating editors, SharedWorker cross-tab sync, sessionStorage drafts, and list-injection from exports.

Legacy JS

  • EmailExportEditor.js — old composer; mostly delegates to React now.

  • EmailedExportedListDetailsModal.js — legacy details + events timeline.

  • EmailSignaturesAdminPanel.js — admin signatures UI.

7. End-to-End Technical Flows

Send a position list

  1. UI calls IsPostmarkActive.

  2. UI calls SendEmail with recipients, subject, HTML body.

  3. Controller calls PostmarkHelper.SendEmailWithEmailTemplateOwnerPositionTemplate.

  4. Postmark MessageID is persisted in the DB via ExportFormatBusiness.

Verify sender signature

  1. First send creates a Postmark sender via CreateSenderSignature.

  2. User confirms the verification email.

  3. IsPostmarkActive flips to true on the next call.

View delivery events

  1. UI calls GetEmailedExportedListDetails.

  2. Controller calls PostmarkHelper.GetMessageDetails with the stored MessageID.

  3. UI renders MessageEvents grouped per recipient.

Resend verification

PostmarkOverlayResendSignatureEmailPostmarkHelper.ResendSignatureEmail

Distribution lists

[ListName] tokens in recipient fields are expanded server-side (FillDistributionLists) before the Postmark call.

8. Summary

Concern

Detail

Integration point

PostmarkHelper (raw HTTP, not the SDK)

Tokens

Account (signatures) and Server (sending / messages)

Templates

16534707 (main) and 23095352 (custom)

Frontend contact

React + legacy JS talk only to two MVC controllers — never directly to Postmark