Postmark

Overview

Integration

User guide

API reference

Webhooks

Parse an email

A server’s inbound mailbox is a unique hash generated when the server is created. Likely you will not share this mailbox identity directly with your customer, but instead set up email forwarding (i.e. from inbound@yourapp.com to yourhash@inbound.postmarkapp.com). You can do this by using Postmark’s Inbound Domain Forwarding or you can read our help article to learn how to configure a custom forwarding email address from your Gmail/Google Apps account. Your unique hash is provided in the InboundHash property of the JSON object returned from the HTTP GET /servers/:serverid request in the Servers API.

If you haven’t already, you may want to configure your inbound server before you get started.

When an email is received by Postmark at your unique mailbox address, it is immediately sent the webhook URL you provided for that mailbox during configuration. The HTTP POST will contain a JSON body similar to the example.

With the JSON data, you can easily process the email data into your application, including all email headers, content, and attachments. It will be up to you to choose how you process the data, since it depends heavily on the use case, programming language, and environment.

Keep in mind, when a hook returns a non-200 code, we will schedule the JSON post for a retry. A total of 10 retries will be made, with growing intervals from 1 minute to 6 hours. If all of the retries have failed, your Inbound page will show the message as Inbound Error.

Code examples

To help you get started, we’ve collected some community developed examples and “mitts” — or code designed to “catch” Postmark Inbound webhook JSON.

Rails Gem
Ruby Gem
PHP
Python
Node.js
.NET

We are actively looking for your contributions for other languages. If you write a code sample or library for Postmark Inbound, please email us at support@postmarkapp.com. Contributions are appreciated and rewarded with free Postmark credits. If you have questions, problems, or ideas for how to improve this feature, we have a low-traffic API developer email list that you can join as well.

Helpful custom information

We add some extra custom data to emails that are processed which can be useful as you process messages. Here is a list and description of each piece of information:

New!StrippedTextReply

When a user replies to a message, and your Inbound address is set as the To recipient, Postmark will attempt to parse the text portion of the reply and display it in the StrippedTextReply field. This simplifies the process of parsing reply messages. Reply parsing will also work when the Inbound address is in the CC field and the message was replied-to-all.

Note: The StrippedTextReply field is limited to English text replies and is currently tested on the following email platforms: Yahoo, iCloud, Gmail, Outlook.com, iOS Mail, Apple Mail, Microsoft Outlook (Windows & Mac), and Mozilla Thunderbird. Postmark makes a “best attempt” to parse all inbound replies.

MailboxHash

If your users send email to an email address that includes a custom hash after the mailbox name (user+hash@yourdomain.com) Postmark will split that hash out into its own field. You can see the example above as "MailboxHash": "ahoy". This is useful for linking replies to outbound messages and other tasks where you want to tie the inbound message to a specific item in your application.

Example JSON response

{
  "From": "myUser@theirDomain.com",
  "FromName": "My User",
  "FromFull": {
    "Email": "myUser@theirDomain.com",
    "Name": "John Doe",
    "MailboxHash": ""
  },
  "To": "451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com",
  "ToFull": [
    {
      "Email": "451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com",
      "Name": "",
      "MailboxHash": "ahoy"
    }
  ],
  "Cc": "\"Full name\" <sample.cc@emailDomain.com>, \"Another Cc\" <another.cc@emailDomain.com>",
  "CcFull": [
    {
      "Email": "sample.cc@emailDomain.com",
      "Name": "Full name",
      "MailboxHash": ""
    },
    {
      "Email": "another.cc@emailDomain.com",
      "Name": "Another Cc",
      "MailboxHash": ""
    }
  ],
  "Bcc": "\"Full name\" <451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com>",
  "BccFull": [
    {
      "Email": "451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com",
      "Name": "Full name",
      "MailboxHash": "ahoy"
    }
  ],
  "OriginalRecipient": "451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com",
  "ReplyTo": "myUsersReplyAddress@theirDomain.com",
  "Subject": "This is an inbound message",
  "MessageID": "22c74902-a0c1-4511-804f2-341342852c90",
  "Date": "Thu, 5 Apr 2012 16:59:01 +0200",
  "MailboxHash": "ahoy",
  "TextBody": "[ASCII]",
  "HtmlBody": "[HTML(encoded)]",
  "StrippedTextReply": "Ok, thanks for letting me know!",
  "Tag": "",
  "Headers": [
    {
      "Name": "X-Spam-Checker-Version",
      "Value": "SpamAssassin 3.3.1 (2010-03-16) onrs-ord-pm-inbound1.wildbit.com"
    },
    {
      "Name": "X-Spam-Status",
      "Value": "No"
    },
    {
      "Name": "X-Spam-Score",
      "Value": "-0.1"
    },
    {
      "Name": "X-Spam-Tests",
      "Value": "DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,SPF_PASS"
    },
    {
      "Name": "Received-SPF",
      "Value": "Pass (sender SPF authorized) identity=mailfrom; client-ip=209.85.160.180; helo=mail-gy0-f180.google.com; envelope-from=myUser@theirDomain.com; receiver=451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com"
    },
    {
      "Name": "DKIM-Signature",
      "Value": "v=1; a=rsa-sha256; c=relaxed/relaxed;        d=wildbit.com; s=google;        h=mime-version:reply-to:date:message-id:subject:from:to:cc         :content-type;        bh=cYr/+oQiklaYbBJOQU3CdAnyhCTuvemrU36WT7cPNt0=;        b=QsegXXbTbC4CMirl7A3VjDHyXbEsbCUTPL5vEHa7hNkkUTxXOK+dQA0JwgBHq5C+1u         iuAJMz+SNBoTqEDqte2ckDvG2SeFR+Edip10p80TFGLp5RucaYvkwJTyuwsA7xd78NKT         Q9ou6L1hgy/MbKChnp2kxHOtYNOrrszY3JfQM="
    },
    {
      "Name": "MIME-Version",
      "Value": "1.0"
    },
    {
      "Name": "Message-ID",
      "Value": "<CAGXpo2WKfxHWZ5UFYCR3H_J9SNMG+5AXUovfEFL6DjWBJSyZaA@mail.gmail.com>"
    }
  ],
  "Attachments": [
    {
      "Name": "myimage.png",
      "Content": "[BASE64-ENCODED CONTENT]",
      "ContentType": "image/png",
      "ContentLength": 4096,
      "ContentID": "myimage.png@01CE7342.75E71F80"
    },
    {
      "Name": "mypaper.doc",
      "Content": "[BASE64-ENCODED CONTENT]",
      "ContentType": "application/msword",
      "ContentLength": 16384,
      "ContentID": ""
    }
  ]
}
Contact with Name

Available fields: FromFull, ToFull, and CcFull

These “full” fields contain the contact info of the sender, recipient and any Cc recipients. The general format of these fields is a JSON object.

The “Full” variation of the contact info contains both the email of the person, as well as their full name and MailboxHash (if they specified it, it is optional). These three pieces of data are in separate fields, so no need to parse anything further. If the message has multiple TO recipients, the ToFull field will be a JSON array containing several JSON documents of the previously stated contents — same goes for the CcFull field.

Example JSON

{
  "Email": "myUser@theirDomain.com",
  "Name": "Full name",
  "MailboxHash": ""
}
LegacyContact

Note: If you are implementing Postmark Inbound, please use the "Full" contact variations as we will deprecate these fields in the future in favor of the Full variation above.

Available fields: From, To and Cc are legacy counter-parts for FromFull, ToFull and CcFull fields.

If multiple people are present in the To or Cc list, they are separated with comma.

Example JSON

\"Full name\" <myUser@theirDomain.com>
Attachments

Postmark will happily support attachments from your senders and include them in the JSON object as a tuple of filename, a MIME content type, and the base64-encoded data.

For example, if a sender attaches an image, cat.jpeg, to their email, this JSON structure is generated.

You will then need to decode the content to generate the original attachment file. Total cumulative size for all Inbound attachment files may not exceed 25 MB.

SpamAssassin headers

By default, Postmark will accept any emails that are sent to your inbound address, including spam. If you’re using Gmail forwarding, their filters will take care of much of this for you. To make it easier to find and filter spam that is sent to your app, we scan all inbound emails using SpamAssassin, and add a few unique headers to the header content.

  • X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on rs-mail1– Know the version of SpamAssassin that checked your inbound email.
  • X-Spam-Status: No– Either Yes or No depending on how SpamAssassin considers the message.
  • X-Spam-Score -0.8– The spam score of the message, ranging from a negative (better) to positive (worse) number. The default consideration of spam is a score above 5.
  • X-Spam-Tests: DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_LOW– The series of tests that were triggered when processing the message. A full list of the test can be found on the SpamAssassin Tests page.

Note: While SpamAssassin is a powerful and effective tool, it is possible that legitimate messages could be marked with a higher score. We recommend thorough testing before filtering spam messages into your app.

Example JSON attachment

{
  "Attachments": [
    {
      "Content": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAwADADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDg7nQ9XlmtLWPTbku9wg5jIA5zknsPevbvBXhjTdPRXvJUubleW5+RT7D+prlRq32WFpWb5+w9qzrTxFMrqyySlyMhR1J/Dmvl62aNyUGtArNqXLE9+tNSs4IFRpB6cdfpWX4slsdQszGzblUhlZTgow6Mp9Qa8lXxFqEeQ0bRrkACVtgyfYkUsmu3sbhneHDHgeepz9eaueLpyp8ljD2VTojSuLhNQeWz1FwL+3+5Oox5qdmI/nWDdu0bPC55U4NUp9Tkn1aGSMoZERg21wfl69vxrT0fTpPEV61skyx3WwtFu4D46g+9eLWwbrNKC978z0sFiJUXyVNvyOR1q4un1E2sTFV3Dn2xzVPxN4qbwwZoI7Ca5iwpMm8ouCDnBHNb99FG80h2jcQPyq9p+lQanb7poEkOMHcM9AAQfyruwc4OpaSuPl/eNI47Rtf0DxKkV4iNFcQuu+OX1/vA/wAQ7ZFHjrxNc6fbQWfh+wM9wSz7kiL+SvQHb6nP6VoeJfDjxTi80i1U/ZQdwGApXHIAHXFS+EdNuJ7U6vfwqUuiDHsJ3bBwMg/nXalBS5re72OhwbjYzfAEfiImK91i6maWSVd8WFVQp4xwOeDzXXeE79tO8W2xHL293sI/2Twf0JrVuLa1t7FZFUgLgjjvmuZmRk8bXAU9HLfjsJrGtUftIzRxV4bF+ewvjI0hsZyFPeM4xT7HUlsLS6OGKqpfjqB3rqvFOszaZZkQ8tIhB78e3vXl15qpgnWGGPDj5pFJyCT2zWMMHKmlNanRUfNLmOl0nVbTW7XYsvkRHtnDGpgINKgCQXO4AdCcjFc5p9iNzTonlbuVQnpV9bIRQt50oZic5zx+FdShJo1UtCZtTfUNXsrN+I3cMwHcDn+lJApk8QXt86OyKG5UZxngH8s0y3gjF0bu3kTzQhVQTwuRjNa/hieDS4/MEglcH98c/eHfHtRTpKVVKWyOetG7uj//2Q==",
      "ContentLength": 1357,
      "ContentType": "image/jpeg",
      "Name": "cat.jpeg",
      "ContentID": ""
    }
  ],
  "Cc": "",
  "Dump": null,
  "From": "\"Some Cat Fan\" <user@internet>",
  "Subject": "Hello World!",
  "To": "76fa022d4a33c45ddf5be8c24c47f4da@inbound.postmarkapp.com"
}
SPF headers

In addition to spam, it is also helpful to know if the identity of the sender is legitimate. SPF verifies if the sending domain approved the source IP that sent the message. We check the from address domain of the email and verify against the SPF record (if it exists), then add a header to the email with a result.

The possible states are:

  • Received-SPF: neutral– A record does not exist and is neither permitted nor denied.
  • Received-SPF: pass– A record exists and the IP is approved for sending email.
  • Received-SPF: softfail– A record exists and the IP is not approved for sending email, but the record states to accept the messages anyways.
  • Received-SPF: fail– A record exists and the IP is not approved for sending email.
Date header

You can easily fetch the exact time when the message was sent by checking out Date field. This will show the time in sender’s local timezone, so you have that info available too. This field contains the Date header fetched from original email, so the exact format of this field matches Date/time specification in RFC 2822

Example SPF result

Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=209.85.160.42; helo=mail-pw0-f42.google.com; envelope-from=support@postmarkapp.com; receiver=451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com