Lob’s website experience is not optimized for Internet Explorer.
Please choose another browser.


Best Practices for Using Lob’s Print & Mail and Address Verification APIs

Design Guidelines

When you send an API request to Lob, you are sending 3 pieces of information: the to address, the from address, and the content. Below are some best practices to follow when designing content for Lob.

Lob Templates

Bleed Area

For example, for postcards you'll notice that the file that you are sending is actually 4.25" x 6.25" instead of 4" x 6". This extra space is what we call the bleed area. The bleed area is included to ensure that artwork gets printed to the edge - artwork gets printed with larger dimensions and is subsequently trimmed down. Although, backgrounds and graphics should be extended into the bleed area as shown with the background in the above example. Not having them extend into the bleed area could result in a white or unprinted border around the edge of the postcard.


Keep all text within the safe zone to make sure no important content is cut off.


On the postcard, there is a space marked off as *RED: INK-FREE AREA*. Anything in this area will not be printed to leave room for the postage and address information.


The sizes of our mail pieces and their respective templates can be found in our documentation. The API will return an error if you input a non-HTML file with the incorrect dimensions.

File Format


We highly recommend all our customers use HTML to design their postcards. You have a lot of flexibility when using HTML and can add merge variables into your HTML to easily generate dynamic content. See the articles below for more information on using HTML:

When designing content with HTML, use inline styles or an internal stylesheet. Do not use an external stylesheet. If you are linking to images, make sure they are at least 300 dpi. Because the content is only being designed for a single size, we recommend using absolute positioning, as shown below.

body {
  width: 6.25in;
  height: 4.25in;
  margin: 0;
  padding: 0;
  background-image: url(https://s3-us-west-2.amazonaws.com/public.lob.com/assets/beach.jpg);
  background-size: 6.25in 4.25in;
  background-repeat: no-repeat;

#safe-area {
  position: absolute;
  width: 5.875in;
  height: 3.875in;
  left: 0.1875in;
  top: 0.1875in;
  background-color: rgba(255,255,255,0.5);


If you decide not to use HTML, PDFs are also a good option. PDFs are generally in a vector format, which means they can be resized without compromising on resolution. It can be also be easily re-opened and edited.

When using PDFs with Lob, you will need to make sure all your fonts are embedded and the PDF is PDF/A compliant.


When using PNGs or JPEGs with Lob, we require a minimum of 300 dpi. The dpi is calculated as (width of image in pixels) / (width of product in inches) and (length of image in pixels) / (length of product in inches). For example: 1275px x 1875px image used to create a 4.25" x 6.25" postcard has a dpi of 300.


This is a raster image format that can have a transparent background. It is generally a higher quality than other image formats.


This is a raster image format that is often used for photographs. It does not allow for a transparent background.


If you upload an image or send us HTML in your API request, Lob will render and host the content. If you are sending at high volumes, we recommend you host the content yourself on a performant file hosting provider such as Amazon S3 and send us a URL to the content in your API request. By removing the file upload from the API request, you'll be able to reduce your API request time.

Rendering Best Practices

To see how your HTML is rendered, create a Test API request either through the API or on the Dashboard. You'll be able to see a PDF proof of exactly what the design looks like. We highly recommend sending yourself a printed piece to verify the mail piece is how you like it.

Example Pieces

Checkout out our Template Gallery for pre-designed templates to help you get started!

Recommended Tools


Lob's HTML renderer is based off of Webkit. For this reason, using the Safari browser will show previews more accurately. For the best results, we recommend submitting test API requests and reviewing the rendered PDF.


We highly recommend using Adobe Illustrator or Adobe Photoshop to design your content. They'll give you the most flexibility and export options.


We recommend using a developer to convert the PDF to HTML. We'd estimate 15 minutes to 2 hours of development time for the average file. Some applications like Adobe Illustrator come with HTML export options, but the automated tools for doing this today aren't great.

What Should I Test as I Integrate Lob?


Before you go live, verify that your mail pieces are rendering correctly. Lob's dashboard will show proofs of the mail piece you create. The proofs are also available in the API response.


  • Verify your PDF is PDF/A compliant
  • Embed all images and fonts


  • Lob's HTML render is based on Webkit, but is highly modified to be used with Lob. Test rendering by using your test API key and checking the dashboard for proofs, instead of designing using the browser.


  • Verify that your images are at least 300 DPI. This will prevent blurry prints.


As you integrate Lob, you may find it useful to view logs of your requests. If you send a request with your test or live API key, both the request and response will be logged here for you to debug with.

HTML Templates

Easily create, view, edit, and manage your postcard, letter, and check HTML templates by saving them within Lob's system.

NOTE: In Live mode, you can only have as many non-deleted templates as allotted in your current Print & Mail Edition. There is no limit in Test mode.

Creating a Template

Templates can be created from your Dashboard. Any parameter that accepts HTML in postcard, letter, or check creation can also accept a template. When creating a template, you need to pass two pieces of information:

  • An optional description
  • The HTML for your template

While it is optional, we highly recommend adding a description so you can easily identify your template within Lob. As for the HTML design, you can start off with a pre-designed template from our gallery or follow these basic guidelines to get started. Templates are compatible with merge variables, which we encourage using for creating dynamic, custom content. For more detailed guidelines and best practices around creating HTML designs, please see our documentation.

Both the description and your HTML can be edited easily afterwards, so don't worry about getting things perfect.

Like all Lob resources, you can create templates in both your Test and your Live Environment. Templates created in the Test Environment will only be usable by postcards, letters, and checks created in the Test Environment, while templates created in the Live Environment will only be usable by postcards, letters, and checks created in the Live Environment. Be sure to work closely with your developer to make sure you are working in the correct environment.

Viewing a Template

Once you've created a template, you'll be able to view it on your Dashboard. You can do a couple things on this page:

  • Find the template's ID
  • Preview the template
  • Make edits to the template
  • Delete the template
  • View old versions of the template


At the top right-hand corner the page is the template ID, which is completely unique to this particular template. The template ID is how you will refer to this template when ultimately creating a postcard, letter, or check.


On this page, you can view the raw HTML for your template, as well as preview the design by clicking the "Preview" button.

When you preview a template, you'll first need to choose a product to preview it as. We will also auto-populate any merge variables we find in your template, which are dictated by {{double_curly_braces}}. After you've filled out these fields, you'll be able to preview your template as a PDF, which will open in a new tab (you may need to unblock pop-ups in the browser of your choice).

On the 2020-02-11 and later API versions, the JSON editor is required if you need to use loops, conditionals, or objects (read more details later in this article). We will not auto-populate any merge variables we find in your template when using the JSON editor.

Keep in mind that you won't be able to see any specific proofing elements such as address information, barcodes, or cropping that happen when actually creating a mailing. Ultimately, the best way to test your template will be to send it as a test postcard, letter, or check.


You can edit a template's description as well as its HTML from the Dashboard as well. When you edit a template's HTML, a new published version of that template will be created. Those changes will go into effect immediately. Any postcard, letter, or check integrations referencing that template ID will reflect the updated changes as soon as they are submitted.


If a template is no longer of use to you, you can delete it from Lob. Once you delete a template, it will no longer be usable in any postcard, letter, or check requests, so make sure that the template is not being actively used in any integrations!


At the top of the page will always be the published version, which is the version that will be used when that template is referenced in a postcard, letter, or check request. Historical versions of the template can be viewed for your reference, along with a unique ID and browser preview for each. This can be helpful for reconciling edits made in the past.

Using a Template in a Postcard, Letter, or Check Request

To test out and use your HTML template, you'll need to create a postcard, letter, or check. For parameter details, please see the respective documentation for each endpoint: Postcards, Letters, Checks).

For this example, we'll create a postcard. Don't forget to add in any merge variables you'd like to interpolate in. Remember that the published version of the template is the version which will be used for the request.

curl https://api.lob.com/v1/postcards \
  -u test_0dc8d51e0acffcb1880e0f19c79b2f5b0cc: \
  -d "description=Demo Postcard job" \
  -d "to=adr_78c304d54912c502" \
  -d "from=adr_61a0865c8c573139" \
  --data-urlencode "front=tmpl_d2ef51761865901" \
  --data-urlencode "back=tmpl_7c9c41753dfea20" \
  -d "merge_variables[name]=Harry" \
  -d "merge_variables[code]=5dks92"

After sending this request, you'll be able to see your final postcard design:

For any postcards, letters, or checks that use templates, the template IDs and version IDs used will be retrievable from the API (see the respective documentation for each endpoint: Postcards, Letters, Checks), as well as viewable on the Dashboard:

Using HTML and Merge Variables

Merge variables make it easy to personalize your postcards, letters, and checks with whatever custom information you'd like. Common use cases for merge variables are:

  • Customer information (name, phone number, etc)
  • Date
  • Invoice line items
  • Custom images


Merge variables only work if you use HTML to generate your mail piece. To make use of a merge variable, insert {{tag_name}} (with double curly braces) into the HTML you pass. You can define whatever variables make sense for the design you are creating.

<html style="padding: 1in; font-size: {{fontsize}}; color: {{color}}">
  <p>Hi {{name}}, how are you?</p>
  <img src="{{img}}" style="width: 1in">

If the HTML above were used with the information below:

name fontsize color img
Harry 20px red https://s3-us-west-2.amazonaws.com/public.lob.com/assets/beach.jpg
Ami 30px green https://s3-us-west-2.amazonaws.com/public.lob.com/assets/jungle.jpg

Then the final two postcards would be rendered like so:

Use this feature to your advantage to create completely custom postcards, letters, and checks for your recipients, without having to generate PDFs on your side.


Note: This is only available on the 2020-02-11 and later API versions.

You have the ability to access merge variables at any depth within your JSON. For instance you can have an object created with a bunch of user information:

  "user": {
    "name": "Ami",
    "location": "San Francisco"

To access these within your template you can do the following:

Name is: {{user.name}}
Location is: {{user.location}}

This renders the following template:

Name is: Ami
Location is: San Francisco

You can also create a "section" which will change the context of the variables you are accessing. For instance:

  Name is: {{name}}
  Location is: {{location}}

This renders the same HTML as above, since you changed the context to the "user" temporarily. This is done by the {{#user}} and {{/user}} syntax. Everything inside that "section" tries to access a property on the "user" object.


Note: This is only available on the 2020-02-11 and later API versions.

Conditionals allow you to conditionally render any sort of content to the user. A simple use case could be you want to send an extra message to any user who has bought over 1,000 products. To do that would require the following:

Merge variables:

  "bought_a_lot": true


Thank you for being a loyal customer!

This renders:

Thank you for being a loyal customer!

If you want to render some content if a condition does not pass, simply do: {{^condition}}{{/condition}}. For example:

Merge variables:

  "bought_a_lot": false


Buy more products!
Buy more products!


Note: This is only available on the 2020-02-11 and later API versions.

Loops allow you to display content multiple times for multiple objects. The following is how you would use it:

Merge variables:

  "users": [{"name": "Nathan"}, {"name": "Ami"}]


  List of top users:

This renders:

  List of top users:

Merge Variable Strictness Setting

Depending on how much control you'd like over your HTML integration, we offer 2 different account settings that affect how we treat merge variables. This account setting affects the POST /v1/postcards, POST /v1/letters, and POST /v1/checks endpoints in both test and live mode:

  • Strict: Lob will send a 422 error if you define a merge variable in your HTML that is not passed in the merge_variables field of that request. Pass ' ' or null to have a particular defined variable not render.
  • Relaxed: Lob will not send an error if you define a merge variable in your HTML that is not included in the merge_variables field of that request. Instead, we will simply render nothing in the HTML in place of that merge variable.

Regardless of your strictness setting, if you pass merge variables keys that are not defined in your HTML, no error will be thrown. Your HTML will simply be rendered as normal without substituting the extra variable(s).

NOTE: On the 2020-02-11 and later API versions which support JSON in merge variables, merge variable strictness will still apply to the nested object keys, i.e. if a nested merge variable is undefined on the strict setting, then Lob will send an error.

Using HTML and Fonts

With Lob's HTML feature, you can easily make use of any font to make your postcards, letters, and checks as on brand as possible. By default, we only support 1 font family, Deja Vu, within our renderer. To access any other fonts, you must follow one of the steps below.


Using Google Web Fonts is a simple way to import and use a wide variety of fonts within your Lob HTML without having to download or upload any assets.

After you've gone to their platform and found a font you'd like to use, all you have to do is include a link to the font in the <head></head> tag of your HTML:

  <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">

Then, you simply reference the font like so in your <style></style> tag (or inline if you prefer):

body {
  font-family: 'Roboto';


Outside of Google Fonts, you are free to use any custom font you desire. Simply upload your font files to a performant file hosting platform (such as Amazon S3) and use CSS's @font-face declaration to load them into your design.

For example, in the <style></style> tag of your HTML, add the rule like so:

@font-face {
  font-family: 'My Font Name';
  font-style: normal;
  src: url('https://link-to-your-font/my_font_name.ttf') format('truetype');

And then reference the font in your <style></style> tag (or inline if you prefer):

body {
  font-family: 'My Font Name';

Page Breaks in HTML

When creating HTML letters within Lob, you may find it necessary to create page breaks in order to split up your content by page. This is easy to do using CSS's page-break-after property.

For example, create a page class in your HTML and apply these styles:

.page {
  page-break-after: always;
  position: relative;
  width: 8.5in;
  height: 11in;

And then, divide the pages of content in your HTML by this page class:

<div class="page">
  Page 1
<div class="page">
  Page 2

Using Metadata

Metadata makes it easier to associate structured data with Lob objects. With metadata you can easily attach key-value pairs to postcards, letters, checks, and other objects. This data could include a campaign name, a customer segment, or an internal id.

Check out our documentation for implementation details.


Metadata objects can include up to 20 key-value pairs of custom data. Each metadata key must be less than 40 characters long and values must be less than 500 characters. Neither can contain the characters " and \. Metadata does not support nested objects.

This data can be searched through and viewed in our dashboard as shown below.

MetaData Filtering

Using the filter button in the dashboard, you can filter metadata.

Metadata Filter Input on Dashboard

Dashboard View

Metadata Filter Applied

Verification of Mailing Addresses

Whenever you send a mailing through Lob, you reap the benefits of accurate address cleansing and verification powered by our CASS-Certified Address Verification API. Verifying addresses is a necessary part of sending mail at scale to optimize the efficiency and accuracy of your mail delivery.

Standardization and Verification

Upon creation, all US addresses will be automatically standardized, cleansed, and verified through our US Address Verification API before being returned back to you. All international addresses will simply be standardized by being transformed to all uppercase to conform with USPS standards.

Strictness Settings

Depending on what business logic you'd like to take based on your verification result, you can toggle your US Address Strictness Settings in your Dashboard. There are 3 possible Strictness Levels:

  • Strict: Only US addresses that Lob and the USPS deem deliverable can be successfully used as the to address for postcard, letter or check resources. This maps to a deliverability value of deliverable (see documentation for more information). Postcard, letter, and check requests sent with non-deliverable addresses will return a 422 (Unprocessable Entity) error. If addresses have never been run through verification (i.e. were created in the past), they will also err when used as the to address.
  • Normal: Postcards, letters, and checks will be created for addresses that Lob and the USPS deem deliverable, as well as addresses for which secondary information is extraneous or missing. This maps to the deliverability values of deliverable, deliverable_unnecessary_unit, deliverable_incorrect_unit, and deliverable_missing_unit (see documentation for more information). Otherwise, you will receive a 422 (Unprocessable Entity) error. If addresses have never been run through verification (i.e. were created in the past), they will also err when used as the to address.
  • Relaxed: All postcard, letter, and check resources will be successfully created and mail delivery will be attempted, regardless of address validity. You will not receive a 422 (Unprocessable Entity) for Address Strictness reasons.

Ultimately, this allows you completely control the destiny of your final pieces. Only want to send to addresses that are guaranteed to be deliverable? Pick "Strict" mode. Confident that your addresses are right and want us to mail them out anyway? Use "Relaxed" mode. We want to give you the power to decide when Lob should be sending mail or when we should be rejecting based on deliverability. Ultimately, this reduces the amount of undeliverable mail you send and saves you money.


National Change of Address (NCOA) is a service offered by the USPS, which allows individuals or businesses who have recently moved to have any mail forwarded from their previous address to their new address.

As a CASS-certified Address Verification Provider, Lob also offers NCOA functionality to our Print & Mail customers. With the Lob NCOA feature enabled, Postcards, Letters, Checks and Addresses can automatically be corrected to reflect an individual's or business' new address in the case that they have moved (only if they have registered for NCOA with the USPS).

Due to privacy concerns and USPS constraints, for customers with NCOA enabled, our API responses for a limited set of endpoints differ slightly in the case when an address has been changed through NCOA (see more below).

NOTE: This feature is exclusive to certain customers. Upgrade to the appropriate Print & Mail Edition to gain access.

Signing a Processing Acknowledgements Form (PAF)

In order to have the Lob NCOA feature enabled, our customers must sign a Processing Acknowledgement Form (PAF), which is required by the USPS. NCOA cannot be enabled if a PAF has not been signed.

Please reach out to your Account Manager or your Customer Success Manager to see if you are eligible to sign a PAF.

Endpoints Affected

With Lob NCOA enabled, there are no changes to API requests sent to Lob. This is true whether you are using our client-facing libraries, or making raw HTTP(S) requests to our API. If you have Lob NCOA enabled, all live API requests to the following endpoints will be run through NCOA after first being cleansed and verified through CASS. However, there are some changes to API responses for the following endpoints:

  • POST /v1/addresses
  • POST /v1/checks
  • POST /v1/letters
  • POST /v1/postcards


Though there are no changes to API requests, there are significant changes to our API responses, but only in the event that an address has been changed through NCOA. If an address has not been changed through NCOA, the response would be identical to our standard API responses, except with the addition of a recipient_moved field, which is false for unchanged addresses.

Due to the USPS constraints mentioned above, if an address has been changed through NCOA, we are required to suppress the following response fields for that address:

  • address_line1
  • address_line2
  • The +4 portion of the ZIP+4 (5-digit ZIP code will still be present).

In addition, if an address has been changed through NCOA, the address will have a recipient_moved: true flag. For more details about the response format, see the NCOA information in our docs.

In addition to our API responses, the suppressed fields will (almost) always be suppressed in other places within the Lob platform as well. This includes:

  • In the PDF proofs and thumbnails generated for Print & Mail requests
  • In Exports for Postcards, Letters, Checks and Addresses resources
  • API Logs & Event Logs
  • Webhooks
  • Dashboard Search

There are two locations where these fields are not suppressed:

  • In the physical mail piece that will be sent to your customer.
  • In an NCOA export from the Lob Dashboard (discussed in more detail below).

The NCOA export is the only way in which you will be able to access the suppressed response data for addresses that have been changed through NCOA.

Accessing Suppressed Data

In order to allow our customers to access NCOA'd data, the USPS has given us the following constraint:

Customers must send at least 100 addresses through NCOA within one week in order to gain access to NCOA'd data.

This means that in order to access this data, you must send at least 100 live API requests in a one week timespan to any of the following endpoints:

  • POST /v1/addresses
  • POST /v1/checks
  • POST /v1/letters
  • POST /v1/postcards

Additionally, the USPS has defined a "week" to be the following time ranges:

  • 1st-7th of the month (inclusive)
  • 8th-14th of the month (inclusive)
  • 15th-21st of the month (inclusive)
  • 22nd-28th of the month (inclusive)
  • 29th-30th or 31st of the month (inclusive, when a month has more than 28 days)

Once you have sent at least 100 live API requests in a one week timespan, you can access suppressed data through an NCOA export, which can be accessed in the Lob Dashboard Settings, under the "Reporting" tab.

Once in the "Reporting" Tab, you can select any week from the previous month or the current month, and generate an export for that week. Additionally, you have the option of only exporting addresses that have been changed during the NCOA process. This option is selected by default, as this tends to be the more useful option.

Once you have clicked the "Export" button, an email should arrive in your inbox with the exported data. Depending on how many requests you've sent and how many addresses have been changed through NCOA, this can take anywhere from a few seconds to a few hours.

The export is a CSV, which has the following fields:

  • id - The Address ID (not the mailpiece ID) for the address that has been changed.
  • name - The name passed with the API request.
  • company - The company passed with the API request.
  • phone - The phone passed with the API request.
  • email - The email passed with the API request.
  • address_line1 - The full, unsuppressed address_line1, which represents the new address for the recipient.
  • address_line2 - The full, unsuppressed address_line2, part of the new address for the recipient.
  • address_city - The city of the recipient's new address.
  • address_state - The state of the recipient's new address.
  • address_zip - The ZIP code (including the +4) of the recipient's new address.
  • address_country - The country of the recipient's new address. Always UNITED STATES.
  • metadata - The metadata associated with this address.
  • date_created - The timestamp this address was created.

One important thing to note, is that the export only includes an address ID, and not a resource (postcard/letter/check) ID. This means that you must keep track of address ID for inline addresses created in Postcard, Letter and Check requests.

Idempotent Requests

Idempotent Requests are requests that can be called many times without producing different outcomes. GET and DELETE requests are idempotent by definition, meaning the same backend work will occur no matter how many times the same request is issued. POST requests, however, are not idempotent. Sending a successful POST request once will result in a newly created object. If you send the same POST request 5 times, you will create 5 resources, assuming none of those requests err. If a network error occurs, there is no deterministic way to ensure the exact number of resources created.

For this reason, we have added a feature that will allow you to safely resend the same POST request to /v1/postcards, /v1/letters, or /v1/checks and ensure that duplicate products are not created. To perform an idempotent POST request, you simply need to provide an additional Idempotency-Key header that uniquely identifies that resource. See our documentation for more specific information.

Generating Idempotency Keys

We suggest using V4 UUID or another appropriately random string, but how you create the unique keys is up to you. For example, if you would like to associate a check with an internal unique ID of the user you are sending the check to, you may use that user id and the transaction date as an idempotency key (as long as you can guarantee the uniqueness of each idempotency key across requests to the same product). This key will expire after 24 hours, meaning if you resend the same request with the same idempotency key after 24 hours, a second resource will be created. Ultimately, it is up to you to make sure that you are appropriately setting the uniqueness of your keys based on your business logic. See resources below for help generating V4 UUIDs in various languages.

Sending Requests on Retry

In case of failure, we recommend following something akin to an exponential backoff algorithm for retrying your idempotent requests. This ensures that you aren't retrying continuously on a downed server, thereby contributing to the issue at hand.


Below are resources we recommend in various languages for generating V4 UUIDs.


Webhooks are an easy way to get notifications on events happening asynchronously within Lob's architecture. Some common use cases for integrating webhooks are:

  • Sending end users notifications about mail traveling through the postal stream
  • Receiving notifications about erroneous scan events, such as "Re-Routed" or "Returned to Sender"
  • Downloading PDF previews or thumbnails automatically once they are rendered
  • Internal logging and reconciliation

When an event occurs within our architecture and you have a webhook subscribed to that event type in that Environment (Test vs. Live), we will attempt to make a POST request with the entire event object to the URL provided. See here for a full list of all available event types that you can subscribe to.

NOTE: In Live mode, you can only have as many non-deleted webhooks as allotted in your current Print & Mail Edition. There is no limit in Test mode.

Receiving a Webhook

To receive webhooks from Lob, you just need to create another endpoint on your web server that will accept a POST request with a content type of application/json. Keep in mind that it will need to be accessible by us so if there's anything that could prevent Lob from accessing it should be disabled for this endpoint. However, we do have recommendations on how to secure your endpoint below.

To confirm delivery of the webhook, Lob expects a 2xx status code returned in a timely manner. We will consider any other status code (or lack of status code) to be erroneous and attempt to retry the delivery. If your webhook endpoint has any additional complex logic to perform, we recommend immediately returning a 2xx to let us know that you don't want to receive this event again, and then performing that logic afterwards. This should aid in preventing unwanted retry attempts caused by unexpected network timeouts.

Any other information sent back to Lob in the response will be captured and stored (regardless of status code) for you to view on your Dashboard, though we won't perform any processing on it. Therefore, we recommend responding with any information that you may find useful for debugging purposes. While you can return anything (including HTML), we've found it most helpful to return a concise JSON object with anything that could be relevant.

Test vs. Live Environment

Events are created in both your Test and Live Environment, and Webhooks can also be created in both. Please note that Webhooks created in the Test Environment will be triggered off events from your Test API Key, while Webhooks created in the Live Environment will be triggered off events from your Live API Key.

Because tracking events only ever exist in Live, these event types can not be subscribed to in the Test Environment. To debug, you can "fake" the sending of these events to your server by using our Debugger.

Integration Tips

When first starting out, we recommend using our Debugger. This tool allows you to trigger a generated event body to your specified URL on command. This should mainly be used to determine JSON structure when integrating. Since the event bodies sent are fake, all IDs and URLs within them are not accessible and do not map to real resources in your account.

Once you've started local development of the web server that will be handling these requests, we recommend using a tool that provides local tunneling, such as ngrok. This allows you to expose your locally running server to the Internet so we can access it without you needing to deploy your application.

Retry Policy

When webhook attempts executed by Lob do not succeed (e.g. do not receive a 2xx status code, hit a SSL/TLS error, DNS issue, etc), we will continue to try to deliver the same event according to a schedule with an increasing back off. This policy is meant to give you time to rectify the issue without losing any events.

We will attempt the webhook at the following intervals until we either receive a 2xx status code or all 8 attempts have been executed:

Attempt # Time since last attempt Time since first attempt Example
1 n/a 0 min 2016-01-01T00:00:00Z
2 1 min 1 min 2016-01-01T00:01:00Z
3 5 min 6 min 2016-01-01T00:06:00Z
4 30 min 36 min 2016-01-01T00:36:00Z
5 1 hr 1 hr 36 min 2016-01-01T01:36:00Z
6 6 hr 7 hr 36 min 2016-01-01T07:36:00Z
7 12 hr 19 hr 36 min 2016-01-01T19:36:00Z
8 24 hr 43 hr 36 min 2016-01-02T19:36:00Z

This means that if a webhook attempt continuously fails, Lob will stop attempting webhook requests 43 hr & 36 min (approximately 1.82 days) after the initial creation of the event.

Disabling Policy

If your webhook endpoint does not consistently respond with a 2xx status code, we will automatically disable the webhook and stop sending events to the endpoint. Webhooks are automatically disabled when there are 5,000 failures after the third retry of each event in the span of 24 hours. You will receive an email notification when the webhook has been disabled.

Disabled webhooks can be re-enabled in the dashboard by editing the webhook.


It's important to note that for every API request you send to Lob, you may get multiple requests back to your server, depending on what event types you have subscribed to. For best results, please make sure your servers will be able to handle the load you expect to get back.


We understand that security is an important concern when granting external access. You need to make sure that Lob is able to access your endpoint, but you don't want anybody being able to access it or your data, so there are certain features that you can enable on your end to ensure this is possible.


We enforce HTTPS for all webhook URLs. Securing your endpoint with TLS ensures all data being sent to and from your server is encrypted. Make sure that you're using a fully-chained certificate, or else the request will never make it to your server and the attempt will fail. To make sure they are fully-chained, you should use the Debugger and see if the request successfully goes through.


Lob webhooks include a signature to allow you to verify their authenticity. Verifying this signature within your webhook endpoints allows you to ensure that the webhooks originate from Lob and not from a third party.

Webhooks include Lob-Signature and Lob-Signature-Timestamp headers. Lob-Signature is generated by computing HMAC-SHA256 on a signature input and a secret. The signature input is generated by concatenating Lob-Signature-Timestamp (as a string), the . character, and the webhook request's JSON payload (as a string). The secret is unique for each webhook and can be found in the details page for the respective webhook in the dashboard.

A static, fixed secret is used for webhooks in the Test Environment and for requests sent out using the webhook debugger. In these cases, the secret used in generating the signature is the string secret.

The addition of the Lob-Signature-Timestamp in the headers and as the input to HMAC-SHA256 allows you to prevent users from performing replay attacks. In a replay attack, an attacker intercepts webhooks and retransmits them. By verifying that the signature is valid and the timestamp is within some tolerance (we recommend 5 minutes), you can ensure that the request is not an older request being duplicated by an attacker.

In order to verify the Lob-Signature and Lob-Signature-Timeout headers, follow the steps below.

Step 1: Prepare the signature input

Concatenate the Lob-Signature-Timestamp (as a string), the . character, and the request body (as a string).

Step 2: Generate the expected signature

Compute the HMAC with SHA-256 using the webhook secret from the dashboard as the key and the signature input as the message. Convert to a string in base-16, hexidecimal format.

Step 3: Compare Signatures

Compare the expected signature with the Lob-Signature header. If the strings are equal, then the webhook is valid.

Step 4: [Optional] Check the timestamp

If you are concerned about replay-attacks, check that Lob-Signature-Timestamp is not older than your tolerance.


You can also use Basic Authentication to guard your endpoint from public access. This can be used in addition to or instead of webhook signature verification. For the best level of security, we highly recommend verifying webhook signatures, rather than relying solely on HTTP Basic Authentication.

When creating your webhook, insert the username and password to the URL using the following format: https://username:password@example.com/webhooks. This will be converted on our end to the appropriate Authorization header when we make the request.


A common feature that is enabled by default for some frameworks is cross-site request forgery. This is an valuable security measure to ensure that authenticated users aren't performing actions that aren't intended. However, having it enabled on your webhook endpoint could prevent our events from being processed. Instead of disabling it completely, you should just exempt this endpoint from CSRF validation.

API Versions

We will always send events based on the API Version on your account at the time of event creation. If your account is set to an older API version but a request is sent with a hard coded header, the Event generated will still be based on the API Version on your account at that time. Event objects in the past will not be updated if you upgrade your API Version - only subsequent events will follow the new Version's structure.

Cancellation Windows

By default, all new accounts have a 5 minute cancellation window for postcards, letters, and checks. Within that timeframe, you can cancel mailings, free of charge. This gives you the flexibility to quickly QA your mailings before they finally get sent off for production.

Editing your Cancellation Windows

Certain customers can customize their cancellation windows in their Dashboard Settings. Upgrade to the appropriate Print & Mail Edition to automatically gain access to this ability. If you have access to this feature, your cancellation window can be anything from 0 minutes (no cancellation window) to up to 3 days.

Keep in mind that when you edit your cancellation window settings, any changes made will only apply to mailings created after the update was made. If you find yourself constantly changing your cancellation window for different use cases, we recommend using our Scheduled Mailings feature instead.

Cancelling Your Mailings

Within your chosen cancellation window, postcards, letters, and checks are cancelable. This means that they will be completely removed from production and that they will not count towards your monthly usage for billing purposes.

To cancel a mailing, either use the API endpoint or cancel the mailing from your Dashboard. In either case, the mailing will only be cancelable if its send_date has not yet passed.

Bypassing Cancellation Window with Scheduled Mailings

Even if you have a cancellation window set on your account, using the Scheduled Mailings feature to schedule a mailing will override your cancellation window and the send_date passed will be used instead.

Not only is this useful for scheduling mailings far off in the future, but it is also handy for completely bypassing any cancellation window you might have and sending one mailing or batch off to production immediately.

Scheduling Mailings for a Future Date

Postcards, letters, and checks can be scheduled up to 180 days in the future. Until that time, mailings can also be canceled. You can use this feature to:

  • Create automated drip campaigns (e.g. send a postcard at 15, 30, and 60 days)
  • Schedule recurring sends
  • Plan your mailing schedule ahead of time

NOTE: This feature is exclusive to certain customers. Upgrade to the appropriate Print & Mail Edition to gain access.

Scheduling a Mailing

Scheduling a mailing for the future just requires one additional parameter: send_date. This specifies a date and time up to 180 days in the future to start processing the mailing. For billing purposes, requests will count towards the month of their send_date, and will not be charged if they are canceled before that time. If your account has a cancellation window set for the resource you are creating, passing a send_date will override that window. For more detailed request information, see our documentation.

Canceling a Mailing

If something changes on your side and you no longer wish to send a mailing that had already been scheduled (such as the customer taking a certain action), you can programmatically cancel those requests as long as the mailing's send_date has not passed. Additionally, you can cancel a mailing from your Dashboard.

Mail Analytics Dashboard

As you start sending higher volumes of mail with Lob, you may have a need to analyze the status and deliverability of your mail on a more aggregate basis. Our Mail Analytics Dashboard gives you full visibility into where the mail you've sent in any given time period sits in the mail stream. You can slice and dice your data by certain filters to view a full deliverability report, all directly from your Dashboard.

To access this feature, sign in to your Dashboard and click the "Mail Analytics" tab in the left navigation bar.

NOTE: This feature is exclusive to certain customers. Upgrade to the appropriate Print & Mail Edition to gain access.

Mail Speed

Lob is the only intelligent platform that tracks each piece of mail as it moves through the USPS delivery process. All mail that is tracked by Lob is presented and aggregated on this Dashboard. The Mail Speed tab shows the number of business days it took from a mail piece's send_date to the date the mail piece was marked as processed_for_delivery by the USPS.

Mail Speed Histogram

The Mail Speed graph respects all applied filters and shows the average and median mail speed for all the mail pieces that fall within the filter. Pieces that have yet to receive a processed_for_delivery tracking event are not counted in the metrics above the graph. Additionally, the graph is for US mail only.

Mail Distribution

The Mail Distribution tab shows the number of mail pieces sent to each US state (and Washington D.C., but excludes US territories). A state's color corresponds to how many mail pieces have been sent to that state. Hovering over a state will provide more details like how many mail pieces have received a processed_for_delivery tracking event, as well as average and median mail speeds (calculated the same way as the Mail Speed tab).

Mail Distribution Map

You can also view the data in a table format. The table shows the same data as the map, where each state's data corresponds to a row. Clicking on a column header will sort the values in that column. To search by state name, click on the search icon and input a value to filter the table.

Mail Distribution Table

Tracking Events

The Tracking Events tab displays a breakdown of all tracking events applicable to the mail pieces within the filter parameters. The main buckets at the top of the Dashboard represent the different stages in your mail's delivery process:

Tracking Events Statistics Summary

Sent: This is a count of non-deleted mailings that you have sent to Lob in the specified time frame based on the date for which a piece was scheduled (the piece's send_date), not the date the API request was made.

Mailed: This bucket is a count of all pieces that have received a Mailed event, which represents handoff to USPS. NOTE: Access to Mailed Events is exclusive to certain customers. Upgrade to the appropriate Print & Mail Edition to gain access.

In Mailstream: This bucket is an aggregate of two tracking events, In Transit and In Local Area. These are the first two scans that a piece will receive—representing that it's in the mailstream and on the way to its destination.

Processed for Delivery: This bucket is represents pieces that have received a Processed for Delivery scan. This scan means that the piece has been greenlit for delivery at the destination's nearest postal facility and will likely be delivered in 1-2 business days.

Re-Routed: If a piece is re-routed due to recipient change of address, address errors, or USPS relabeling of barcode/ID tag area, it will receive this scan.

Returned to Sender: A piece will receive a Returned to Sender scan if delivery was attempted, but failed due to barcode, ID tag area, or address errors.

Each mail piece will be counted once in each bucket for which it receives a scan. For example, if a letter has these tracking events:

  • "In Transit"
  • "In Local Area"
  • "Processed for Delivery"

Then it will be counted once in the "Sent", "In Mailstream", and "Processed for Delivery" buckets.

NOTE: Certified and Registered letters and Overnight checks are excluded from this dashboard because they are tracked via their own external tracking numbers.

Exporting Tracking Events

You can also export all tracking events data into a CSV file. This can easily be done by accessing the Filters and Export buttons at the top of any list page.

From the list view, use the filters to select the type of mail items that you would like to export its tracking data. You can specify date range, metadata tags, and several other attributes that differ by form factor. Lob currently supports tracking no more than 250,000 mail items per export. Once your selection is made, click “Apply”.

Next, click the "Export" button at the top of the page, and confirm your filter selections. Click yes to make a granular selection of associated tracking events. Once done, submit your request.

You will receive an email with a link to download a CSV file of your requested tracking events.


On your Mail Analytics Dashboard, you can slice-and-dice your data by various other aspects of your mailings, including: product type, recipient ZIP code, template used, and more. You can use these filters to compare deliverability between your different campaigns, recipients, and more.

Metadata Filtering Example

Be sure to leverage Lob's metadata feature to more deeply tag your Lob mailings with any internal data you may have, such as a customer ID or campaign ID. Then, you can easily filter by that metadata within the Analytics Dashboard to view different deliverability reports based on those factors.

For mail sent to international destinations, we only expect to get either "In Transit" or "In Local Area" scans. For this reason, we've added a top level filter where you can compare US and international mailings, with the last three tracking buckets disabled for international mail.

Reading the Graph

Under the tracking event statistics on the dashboard, we also provide a graph that provides an even further breakdown of your mail by day, week, or month. Time periods are grouped by the date a piece was sent, not by the date of the API request. As with all other dates within the Lob system, the UTC time zone is used.

Hover over a specific column to view a full tracking breakdown for that time period. Use this breakdown to narrow down your data for even further analysis.

Tracking Events Line Graph


Over time, you can expect to see tracking events for 98-100% of your mailings. The numbers below are benchmarks for when you should start seeing scans. Keep in mind that these timings and percentages may vary based on the volume of mail you've sent, where you're sending to, and the quality of your address set.

For first class mail, you'll start to see mailings reach the "In Mailstream" bucket in 2-3 business days and the "Processed for Delivery" bucket 1 business day afterwards. After about 4 business days total, you should expect almost all of your mailings to be in the "In Mailstream" state. After about 5 business days total, almost all of your mailings should be in the "Processed for Delivery" state.

For standard mail, which is inherently slower, the timing is a bit different. You'll start to see mailings reach the "In Mailstream" bucket in 4-5 business days and the "Processed for Delivery" bucket 2 business days afterwards. After about 10-11 business days total, you should expect almost all of your mailings to be in the "In Mailstream" state. After about 12-13 business days total, almost all of your mailings should be in the "Processed for Delivery" state.