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 to 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. If we receive a 403 response, we will stop retries. 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. When POSTing, Postmark will wait 2 minutes for a response from a server.

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
.NET
JavaScript

We are actively looking for your contributions for other languages. If you write a code sample or library for Postmark Inbound, send us a message. Contributions are appreciated and rewarded with discounts towards Postmark.

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:

StrippedTextReply

When a user replies to a message, and your Inbound address is set as the To recipient, Postmark will attempt to parse the plain 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. The StrippedTextReply field will only be populated if the inbound message was a reply and included either an "In-Reply-To" header or a "References" header.

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. We also cannot parse HTML email parts of replies to populate this field - it will only be applicable when there is a plain text email part in the reply. 

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",
  "MessageStream": "inbound",
  "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@inbound.postmarkapp.com>",
  "BccFull": [
    {
      "Email": "451d9b70cf9364d23ff6f9d51d870251569e@inbound.postmarkapp.com",
      "Name": "Full name",
      "MailboxHash": ""
    }
  ],
  "OriginalRecipient": "451d9b70cf9364d23ff6f9d51d870251569e+ahoy@inbound.postmarkapp.com",
  "ReplyTo": "myUsersReplyAddress@theirDomain.com",
  "Subject": "This is an inbound message",
  "MessageID": "22c74902-a0c1-4511-804f-341342852c90",
  "Date": "Thu, 5 Apr 2012 16:59:01 +0200",
  "MailboxHash": "ahoy",
  "TextBody": "[ASCII]",
  "HtmlBody": "[HTML]",
  "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: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: FromFullToFull, 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 TOrecipients, 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": ""
}

Legacy Contact

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: FromTo and Cc are legacy counter-parts for FromFullToFull 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. The attachments field will always be an array, so make sure you are accessing the array element to get the content for a specific attachment. 

Let's say the inbound message had a single attachment, you would access the content of that attachment with something like Attachments[0].Content, depending on your programming language you are using. 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 35 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, our system will attempt to scan 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.

Also, there may be instances where our system is unable to run those SpamAssassin scans as we process an inbound message. In those cases, the SpamAssassin headers will not be present or may be incomplete.

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.

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

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

How BCC recipients are handled

For privacy reasons we do not place BCC information in the Raw Source tab within the Postmark UI or provide it within the JSON since the BCC header would not be present if the email was received normally (e.g., you wouldn't see a BCC recipient on an inbound email). There are instances where we do include the BCC recipient within the JSON depending on the following conditions:

  • If the inbound address is the BCC address, it will appear as a BCC in the inbound JSON.
  • If the inbound address is both the To and BCC address, it’ll only appear as a To in the inbound JSON.
  • If the inbound address is the To address and another address is the BCC, the BCC will not appear in the inbound JSON.
  • If the BCC address is your inbound forwarding address/domain and is different than the TO address it'll appear in the JSON.