How to Use EasyPost's Webhooks
This guide will show you how to receive webhooks/event notifications for various EasyPost services.
Several types of objects are processed asynchronously in the EasyPost system (Batches, Trackers, etc.). In order to update users with the status of these background tasks, EasyPost dispatches a webhook event whenever a new Event occurs. Webhooks are push notifications, or callbacks, which allow users to stay up-to-date on the status of their EasyPost objects without needing to poll for updates. Whenever a webhook is triggered, an Event is sent via HTTP POST to each configured webhook URL. Each of these webhooks expect a successful response; in the case of a failure, the EasyPost system will attempt to retry the webhook.
In order to take advantage of webhooks, all you need to do is add your webhook urls to your account page. The way these webhook Events are processed will be specific to your application, but below is an example of a request's JSON body and a simple Sinatra application that removes problematic shipments from a Batch.
A great way to learn more about the contents of each webhook Event is by using an endpoint mocking service such as Beeceptor. This would allow you to create a simple endpoint that you can configure to collect webhook requests made by EasyPost and inspect their contents. We don't use Beeceptor internally so please do research before sending any actual user data to them.
{
"mode": "production",
"description": "batch.created",
"previous_attributes": { "state": "purchasing" },
"pending_urls": ["example.com/easypost-webhook"],
"completed_urls": [],
"created_at": "2015-12-03T19:09:19Z",
"updated_at": "2015-12-03T19:09:19Z",
"result": {
"id": "batch_...",
"object": "Batch",
"mode": "production",
"state": "purchased",
"num_shipments": 1,
"reference": null,
"created_at": "2015-12-03T19:09:19Z",
"updated_at": "2015-12-03T19:09:19Z",
"scan_form": null,
"shipments": [
{
"batch_status": "postage_purchased",
"batch_message": null,
"id": "shp_a5b1348307694736aaqqqq8fqda53f93"
}
],
"status": {
"created": 0,
"queued_for_purchase": 0,
"creation_failed": 0,
"postage_purchased": 1,
"postage_purchase_failed": 0
},
"pickup": null,
"label_url": null
},
"id": "evt_...",
"object": "Event"
}
{
"description": "tracker.updated",
"mode": "test",
"previous_attributes": {
"status": "pre_transit"
},
"created_at": "2022-10-26T20:18:21.000Z",
"pending_urls": [],
"completed_urls": [],
"updated_at": "2022-10-26T20:18:21.000Z",
"id": "evt_55a53eb2556b11ed8945059f515d2b6d",
"user_id": "user_060ab38db3c04ffaa60f262e5781a9be",
"status": "pending",
"object": "Event"
}
require 'easypost'
require 'sinatra'
post '/easypost-webhook' do
result = params['result']
case result['object']
when 'Batch'
batch = client.batch.create(result)
case batch.state
when 'purchase_failed'
batch.shipments.each do |shipment|
if shipment.batch_status == 'postage_purchase_failed'
client.batch.remove_shipments(batch.id, shipments: [shipment])
end
end
end
end
end
Our recommended best practice for securing Webhooks involves either HMAC validation which has first-class support in each of our client libraries or using basic authentication and HTTPS on your endpoint. This will help prevent any altering of any information communicated to you by EasyPost, prevent any third parties from seeing your webhooks in transit, and will prevent any third parties from masquerading as EasyPost and sending fraudulent data. EasyPost performs certificate validation and requires any TLS-enabled (HTTPS) webhook recipients to have a certificate signed by a public trusted certification authority. We do not support sending webhooks over SSLv2, SSLv3, or any connection using so-called export-grade ciphers. For documentation on how to set up your server with TLS, we recommend Mozilla's guide to Server-Side TLS and Qualys's SSL/TLS deployment best practices guide.
Securing a webhook via HMAC validation is simple. Pass a webhook_secret
with your request to create or update a webhook as shown in the example below. Once a webhook secret is set up, we will return its signature via the X-Hmac-Signature
header on every event sent to your webhook URL. All that's left is to validate that the signature we sent you matches the webhook secret you initially sent us. You can achieve this by calling the validate_webhook()
function if using one of our client libraries (the function's name may differ per programming language), and passing in your webhook secret, headers, and the event body. If the signatures match, the function will return the webhook data; otherwise, it will throw an error to protect your system from the incoming webhook.
Basic authorization requires that a username and password combination along with the webhook URL be passed during webhook creation. An example may look like this: https://username:secret@www.example.com/easypost-webhook
. When an event triggers in our system, we'll deliver a webhook to this endpoint along with an Authorization header that you will need to validate the credentials for. If the credentials do not match what we have stored in our system for this webhook (the basic auth header sent to this endpoint), the webhook should be rejected.
1curl -X POST https://api.easypost.com/v2/webhooks \
2 -u "EASYPOST_API_KEY": \
3 -H 'Content-Type: application/json' \
4 -d '{
5 "webhook": {
6 "url": "example.com"
7 }
8 }'
After 6 failures, EasyPost will no longer attempt to send the webhook. There is an increasing delay between retries.
You should return a status code of 2XX
. A 200
is preferred.
You must respond within 7 seconds. If no response is sent back, the webhook Event will be considered a failure and it will be sent again. It is a best practice to receive the webhook and send the Event to be processed by a background worker; this allows you to immediately return a successful response so you do not receive the webhook a second time.
All asynchronous actions that trigger webhooks in Production will also trigger webhooks in Test. For example, you could create a Test Tracker or purchase a Test Shipment. If you are developing locally and need a public URL for your webhooks, you can set one up at beeceptor or a similar endpoint mocking service.
In order to receive webhooks for packages not shipped through EasyPost, all you need to do is create an EasyPost Tracker object with the desired tracking code. Tracker objects send webhooks whenever new tracking Events are detected.
We'll automatically retry failed attempts on your webhook, but currently, there is not a way to retry failed events manually.
Most users use 1-5, but you can set up to 30 webhook endpoints. If you need more, just let us know.