Gareth Wilson Gareth Wilson

Guide to USPS Webhooks: Features and Best Practices

Published


USPS webhooks notify your systems about package tracking activity: a tracked mailpiece hits a scan event or status update. If you're building shipment tracking on the USPS Subscriptions-Tracking API, webhooks are how you receive updates without polling the tracking endpoint.

This guide covers how USPS tracking webhooks work, the optional verification (there's none by default), the stringified payload, the subscription behavior, and the best practices for production.

What are USPS webhooks?

USPS's Subscriptions-Tracking API lets you create a subscription with a listenerURL and filterProperties (by MID or tracking number); USPS then POSTs tracking notifications to that URL. Managing subscriptions uses OAuth2 client-credentials, but that OAuth token is not applied on delivery.

The important thing to understand up front: per-message verification is optional. If you don't configure it, there's no verification on delivery at all, which makes USPS a natural place to add a verification layer in front of your endpoint.

USPS webhook features

FeatureDetails
ConfigurationPOST /subscriptions with listenerURL + filterProperties (MID or tracking number)
Subscription authOAuth2 client-credentials (for management only, not delivery)
Verification (optional)X-HMAC header: Base64(HMAC-SHA256(secret, timestamp + payload)), only if you set a 32-char secret; or IP allowlist; or nothing
Envelope{ subscriptionId, subscriptionType, timestamp, payload, links }
payloadA stringified JSON that must be parsed again
Event filterCurrently only ALL_UPDATES
DeliveryNo documented retry; unreachable listener set to SUSPENDED
LifecycleAuto-delete after 30 days of inactivity (warning at 25); max 10 listener URLs per Home CRID
SDKNone

Events and payload

The event filter enum currently exposes only ALL_UPDATES, so a subscription delivers all tracking updates for the matched MID or tracking number. The notification envelope is:

{
  "subscriptionId": "...",
  "subscriptionType": "TRACKING",
  "timestamp": "...",
  "payload": "{\"trackingNumber\":\"...\",\"trackingEvents\":[...]}",
  "links": []
}

Note that payload is a stringified JSON, not a nested object, so you JSON.parse the envelope, then parse payload again. The payload is semi-thin (a status summary plus tracking events); call the Tracking API for full detail if needed.

Setting up USPS webhooks

Create a subscription with POST /subscriptions (OAuth2 client-credentials from https://api.usps.com/oauth2/v3/token), providing a listenerURL and filterProperties. Optionally set a 32-character secret to enable HMAC signing, and/or rely on IP allowlisting of USPS source IPs. You can register up to 10 listener URLs per Home CRID.

Securing USPS webhooks

Verification is optional, and there are three postures:

  1. HMAC (if you set a secret): USPS sends an X-HMAC header (deprecated alias hmac-header) containing Base64(HMAC-SHA256(secret, timestamp + payload)), where payload is the raw stringified JSON. Recompute and compare.
  2. IP allowlist: restrict the endpoint to USPS source IPs.
  3. Nothing: if you set no secret and no allowlist, there is no per-message verification at all.

Set the secret. Here's the HMAC check:

const crypto = require("crypto");

const SECRET = process.env.USPS_HMAC_SECRET; // 32 chars

app.post("/webhook", express.json(), (req, res) => {
  const { timestamp, payload } = req.body; // payload is a STRING
  const message = timestamp + payload;
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(message)
    .digest("base64");

  const a = Buffer.from(expected);
  const b = Buffer.from(req.headers["x-hmac"] || "");
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  const tracking = JSON.parse(payload); // second parse
  processQueue.add(tracking); // process asynchronously
});

The same check in Python:

import base64
import hashlib
import hmac
import os

SECRET = os.environ["USPS_HMAC_SECRET"].encode()


def verify(timestamp: str, payload: str, header: str) -> bool:
    digest = hmac.new(SECRET, (timestamp + payload).encode(), hashlib.sha256).digest()
    expected = base64.b64encode(digest).decode()
    return hmac.compare_digest(expected, header or "")

USPS webhook limitations and pain points

No verification unless you configure it

The Problem: By default there's no per-message verification. Without a secret or IP allowlist, any POST to your listenerURL looks legitimate, and the OAuth token used to manage subscriptions isn't applied to deliveries.

Why It Happens: USPS makes both the HMAC secret and IP allowlisting opt-in.

Workarounds:

  • Set a 32-char secret and verify X-HMAC, and/or restrict the endpoint to USPS source IPs. Keep the listenerURL secret.

How Hookdeck Can Help: Hookdeck gives you a dedicated ingestion URL and can enforce its own verification and filtering in front of your endpoint, adding a verification layer USPS leaves optional.

The payload is a stringified JSON

The Problem: payload is a JSON string inside the envelope, not a nested object, and the HMAC is over timestamp + payload (the raw string). Parsing it into an object and re-stringifying (or signing the wrong thing) breaks verification and processing.

Why It Happens: USPS nests the tracking data as a string and signs that exact string.

Workarounds:

  • Verify over timestamp + payload using the raw payload string, then JSON.parse(payload) for processing.

How Hookdeck Can Help: Hookdeck can transform the payload into a clean nested structure before it reaches your app, so downstream code isn't double-parsing.

No retries; SUSPENDED and auto-delete

The Problem: There's no documented retry. An unreachable listener is set to SUSPENDED, and a subscription auto-deletes after 30 days of inactivity (with a warning at 25). A missed delivery may simply be gone, and an idle subscription disappears.

Why It Happens: USPS favors suspending/cleaning up over retrying.

Workarounds:

  • Keep the endpoint healthy, reconcile via the Tracking API for anything critical, and monitor subscription state.

How Hookdeck Can Help: Hookdeck durably stores and retries deliveries on a schedule you control, and its observability surfaces a suspended or lapsed subscription as an alert.

Limited event filtering

The Problem: The event filter enum currently exposes only ALL_UPDATES, so you can't narrow to specific scan types at the subscription level; you filter downstream.

Why It Happens: USPS exposes a single catch-all filter today.

Workarounds:

  • Filter events in your handler by the parsed tracking data.

How Hookdeck Can Help: Hookdeck's filters let you route only the tracking events you care about to each destination.

Best practices

Set a secret and verify X-HMAC over timestamp + payload

Configure a 32-char secret, compute Base64(HMAC-SHA256(secret, timestamp + payload)) over the raw payload string, and compare in constant time. Optionally add IP allowlisting.

Parse payload twice

Parse the envelope, then JSON.parse the payload string.

Acknowledge fast, process asynchronously

Return 200 immediately and defer work to a queue. See why to process webhooks asynchronously.

Reconcile and monitor subscriptions

Since there's no retry, reconcile via the Tracking API, and watch for SUSPENDED status and the 30-day auto-delete.

Make USPS webhooks production-ready

Hookdeck adds the verification USPS leaves optional, normalizes the payload, and durably queues every tracking update

Conclusion

USPS tracking webhooks are subscription-based with optional verification: an X-HMAC over timestamp + payload if you set a secret, IP allowlisting, or nothing at all. Add to that a stringified payload you parse twice, no documented retries, and subscriptions that suspend or auto-delete, and the reliability and security work is largely yours.

Hookdeck adds the verification USPS leaves optional, normalizes the payload, deduplicates, and durably queues every tracking update at the edge, so your app processes trustworthy, clean events.

Get started with Hookdeck for free and handle USPS webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.