When you send an email with open tracking, Postmark will store information when the recipient first opens and reads that email. Postmark displays that information in the Postmark application for your server. You can also pull the message opens using the API. For some applications, it’s easier to set a webhook URL for Postmark to push the data to your application as soon as Postmark gets it. An open webhook can instantly notify your application of message opens by POSTing the data in a JSON document to the webhook URL that you specify.
To know every time a recipient opens an email, you need to use the open webhook. If a recipient opens an email multiple times, Postmark will only save the first open. This is what is displayed on the Postmark website and queried through the message opens API. When the open webhook is saved with PostFirstOpenOnly set to false Postmark will POST the open information to your webhook URL every time an open occurs. This is the only way to get information about every open that occurs.
When logged into Postmark, select the Server, the Message Stream, and then go to the Webhooks tab. Choose Add webhook and input your webhook URL in Webhook URL and then select the Open checkbox.
You can modify the Open field using the Webhooks API to edit an existing Webhook. You can also use the Webhooks API to create webhooks and set the Open field at the same time. After setting the URL, verify the webhook so you know it's reachable before events start flowing. See Verifying your webhook.
An example of the full JSON document that would be POSTed to your webhook URL is to the right.
Recipient—the email address of the recipient who opened the email.FirstOpen—if this open was the first time the recipient opened this email.Geo—this JSON object contains geographic information Postmark was able to parse from the IP address the open request came from. This object may only be partially populated in certain cases.Metadata—custom metadata that was included in the email.ReceivedAt—the timestamp of when a message was opened.{
"RecordType": "Open",
"MessageStream": "outbound",
"FirstOpen": true,
"Client": {
"Name": "Chrome 140.0.7339.80",
"Company": "Google",
"Family": "Chrome"
},
"OS": {
"Name": "macOS 26 Tahoe",
"Company": "Apple Computer, Inc.",
"Family": "macOS"
},
"Platform": "WebMail",
"UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.80 Safari/537.36",
"ReadSeconds": 5,
"Geo": {
"CountryISOCode": "US",
"Country": "United States",
"RegionISOCode": "TX",
"Region": "Texas",
"City": "Houston",
"Zip": "77058",
"Coords": "29.5502,-95.0973",
"IP": "203.0.113.42"
},
"MessageID": "883953f4-6105-42a2-a16a-77a8eac79483",
"Metadata": {
"PropA": "some value",
"PropB": "some value"
},
"ReceivedAt": "2026-11-05T16:33:54.9070259Z",
"Tag": "welcome-email",
"Recipient": "margareth@nasa.com"
}
If you’re developing on your local machine or don’t have a public URL for your API, the cURL request example below sends a test webhook to your service. Replace <your-webhook-url>, run the command, and verify it accepts and processes the event as expected.
curl <your-webhook-url> \
-X POST \
-H "Content-Type: application/json" \
-d '{
"RecordType": "Open",
"MessageStream": "outbound",
"FirstOpen": true,
"Client": {
"Name": "Chrome 140.0.7339.80",
"Company": "Google",
"Family": "Chrome"
},
"OS": {
"Name": "macOS 26 Tahoe",
"Company": "Apple Computer, Inc.",
"Family": "macOS"
},
"Platform": "WebMail",
"UserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7339.80 Safari/537.36",
"ReadSeconds": 5,
"Geo": {
"CountryISOCode": "US",
"Country": "United States",
"RegionISOCode": "TX",
"Region": "Texas",
"City": "Houston",
"Zip": "77058",
"Coords": "29.5502,-95.0973",
"IP": "203.0.113.42"
},
"MessageID": "883953f4-6105-42a2-a16a-77a8eac79483",
"Metadata": {
"PropA": "some value",
"PropB": "some value"
},
"ReceivedAt": "2026-11-05T16:33:54.9070259Z",
"Tag": "welcome-email",
"Recipient": "margareth@nasa.com"
}'