Gareth Wilson Gareth Wilson

Guide to AfterShip Webhooks: Features and Best Practices

Published


AfterShip is the leading shipment tracking platform, used by thousands of e-commerce businesses to monitor deliveries across 1,200+ carriers worldwide. Beyond its tracking dashboard, AfterShip's webhook system enables developers to receive real-time notifications whenever a shipment's status changes, powering custom tracking pages, automated customer communications, and fulfillment workflows.

This guide covers everything you need to know about AfterShip webhooks: their features, how to configure them, best practices for production deployments, and the common pain points developers face along with solutions to address them.

What are AfterShip webhooks?

AfterShip webhooks are HTTP callbacks that deliver shipment tracking updates to your endpoints whenever a tracking event occurs. When a package changes status (from label creation through to final delivery) AfterShip sends a JSON payload via HTTP POST containing the full tracking details to URLs you configure. This enables integration with order management systems, customer notification platforms, analytics pipelines, and any service that can receive HTTP requests.

AfterShip provides webhooks across several of its products:

  • AfterShip Tracking — The primary webhook integration, sending updates whenever shipment tracking status changes across 1,200+ carriers.
  • AfterShip Shipping — Webhooks triggered by shipping label events such as label creation and shipment updates.
  • AfterShip Returns — Webhooks for return request lifecycle events including creation, approval, and completion.
  • AfterShip Warranty — Webhooks for warranty claim status updates.

This guide focuses primarily on Tracking webhooks, as they're the most commonly used webhook integration.

AfterShip webhook features

FeatureDetails
Webhook configurationAfterShip admin dashboard (Notifications — Webhooks)
Hashing algorithmHMAC-SHA256 (base64-encoded)
Delivery methodHTTP POST
Retry logicUp to 14 attempts with exponential backoff (2^n × 30s)
Maximum webhook URLs10 per account
Custom headersUp to 5 per URL
Allowed ports80, 443, 8080
Manual retryNot available
Browsable delivery logNot available
Webhook versioningIndependent of API version; configurable per URL
Test webhookAvailable via dashboard

Supported tracking statuses

AfterShip webhooks notify you of the following shipment status changes:

StatusDescription
InfoReceivedCarrier has received the shipment information but not yet picked up the package
InTransitShipment is in transit to the destination
OutForDeliveryShipment is out for delivery in the final mile
AttemptFailDelivery attempt failed (e.g., no one available to receive the package)
DeliveredShipment has been successfully delivered
AvailableForPickupShipment is available for pickup at a designated location
ExceptionShipment has encountered an exception (e.g., customs hold, damage, address issue)
ExpiredTracking information has expired with no further updates from the carrier
PendingTracking information is not yet available from the carrier

Each webhook delivery includes the current tracking status along with the full checkpoint history, allowing your endpoint to take appropriate action based on the shipment's lifecycle.

Webhook payload structure

When AfterShip sends a webhook notification, it delivers a JSON payload via HTTP POST. The payload includes the full tracking object with all available details:

{
  "event_id": "a]b1c2d3-e4f5-6789-abcd-ef0123456789",
  "event": "tracking_update",
  "msg": {
    "id": "t1r2a3c4k5i6n7g8",
    "tracking_number": "1Z999AA10123456784",
    "slug": "ups",
    "title": "Order #12345",
    "tag": "InTransit",
    "subtag": "InTransit_002",
    "subtag_message": "In transit to destination",
    "order_id": "12345",
    "order_number": "#12345",
    "custom_fields": {
      "store_name": "My E-Commerce Store"
    },
    "checkpoints": [
      {
        "slug": "ups",
        "tag": "InTransit",
        "subtag": "InTransit_002",
        "subtag_message": "In transit to destination",
        "message": "In Transit",
        "location": "Louisville, KY",
        "country_iso3": "USA",
        "city": "Louisville",
        "state": "KY",
        "postal_code": "40213",
        "checkpoint_time": "2026-02-14T10:30:00",
        "coordinates": []
      }
    ],
    "courier_estimated_delivery_date": {
      "estimated_delivery_date": "2026-02-16",
      "confidence_code": null,
      "estimated_delivery_date_min": "2026-02-15",
      "estimated_delivery_date_max": "2026-02-17"
    }
  }
}

Key payload fields

FieldDescription
event_idUnique identifier for this webhook event
eventEvent type (e.g., tracking_update)
msg.idAfterShip's internal tracking ID
msg.tracking_numberThe carrier tracking number
msg.slugCarrier identifier (e.g., ups, fedex, dhl)
msg.tagCurrent tracking status (e.g., InTransit, Delivered)
msg.subtagGranular status code providing more detail
msg.subtag_messageHuman-readable description of the subtag
msg.checkpointsArray of all tracking checkpoints with timestamps, locations, and status details
msg.custom_fieldsCustom key-value pairs you attached when creating the tracking
msg.courier_estimated_delivery_dateCarrier or AI-predicted estimated delivery date with confidence level

AfterShip explicitly warns that webhook enum values (like tag and subtag) are not restrictive and may be extended with new options as the product evolves. You should treat these values as strings rather than fixed enums to avoid crashes when new values appear.

Security with HMAC signatures

AfterShip signs every webhook request (v4.3 and above) using HMAC-SHA256 to verify authenticity. Each webhook request includes an aftership-hmac-sha256 header containing a base64-encoded signature generated from the request body and your account's webhook secret.

You should verify this signature on every incoming webhook to ensure the request genuinely originated from AfterShip and hasn't been tampered with.

AfterShip's Shipping product uses a different header name: am-webhook-signature. Ensure you're checking the correct header for the product you're integrating with.

Authentication options

AfterShip webhooks support multiple authentication methods via custom headers:

MethodConfiguration
HMAC-SHA256 SignatureBuilt-in aftership-hmac-sha256 header on all requests
Basic AuthenticationCustom header with username/password credentials
Bearer TokenCustom header with Authorization: Bearer <token>
Secret KeyCustom header with a shared secret value

You can add up to 5 custom headers per webhook URL to support your preferred authentication scheme.

Webhook versioning

AfterShip maintains a versioned webhook system that operates independently of the API version. This means you can use API version 2026-01 while your webhooks continue to deliver payloads in the 2025-07 format.

Key versioning details:

  • Each webhook URL can be configured with a specific webhook version.
  • An as-webhook-version request header is included in every webhook request, indicating the payload version.
  • When a webhook version reaches end-of-life, AfterShip automatically migrates it to the oldest supported version.
  • AfterShip releases new API and webhook versions roughly every six months, with a migration window before older versions are deprecated.

This versioning approach allows you to test new payload formats against specific URLs before migrating your production integration.

Setting up AfterShip webhooks

Via the AfterShip dashboard

  1. Log in to your AfterShip admin dashboard.
  2. Navigate to Notifications — Webhooks.
  3. Select the webhook version (the latest version is recommended).
  4. Enter your webhook URL endpoint.
  5. Optionally add custom headers (up to 5) for authentication.
  6. Click Send test webhook to validate your endpoint.
  7. Save your configuration.

You can add up to 10 webhook URLs, allowing you to route events to multiple systems simultaneously.

Via the Tracking API

You can also manage tracking and receive updates programmatically using AfterShip's Tracking API. While webhooks are configured through the dashboard, the API lets you create trackings, retrieve status updates, and set custom fields that appear in webhook payloads.

AfterShip provides official SDKs for Java, Node.js, .NET, Python, Ruby, PHP, and Golang to simplify API integration.

Best practices when working with AfterShip webhooks

Verifying HMAC signatures

When processing webhooks from AfterShip, verify the HMAC signature to ensure requests genuinely originated from AfterShip and haven't been tampered with.

Node.js

const crypto = require('crypto');
const express = require('express');
const app = express();

app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

function verifyAfterShipSignature(rawBody, signature, secret) {
  const computedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('base64');

  return crypto.timingSafeEqual(
    Buffer.from(computedSignature),
    Buffer.from(signature)
  );
}

app.post('/webhooks/aftership', (req, res) => {
  const signature = req.headers['aftership-hmac-sha256'];

  if (!signature || !verifyAfterShipSignature(
    req.rawBody, signature, process.env.AFTERSHIP_WEBHOOK_SECRET
  )) {
    return res.status(401).send('Invalid signature');
  }

  // Acknowledge immediately, process asynchronously
  res.status(200).send('OK');

  processTrackingUpdate(req.body);
});

Python

import hmac
import hashlib
import base64
import os
from flask import Flask, request, abort

app = Flask(__name__)

def verify_aftership_signature(payload, signature, secret):
    computed = base64.b64encode(
        hmac.new(
            secret.encode('utf-8'),
            payload,
            hashlib.sha256
        ).digest()
    ).decode('utf-8')

    return hmac.compare_digest(computed, signature)

@app.route('/webhooks/aftership', methods=['POST'])
def handle_aftership_webhook():
    signature = request.headers.get('aftership-hmac-sha256')

    if not signature or not verify_aftership_signature(
        request.get_data(),
        signature,
        os.environ['AFTERSHIP_WEBHOOK_SECRET']
    ):
        abort(401)

    # Acknowledge immediately
    # Process asynchronously in production
    return 'OK', 200

Respond quickly and process asynchronously

AfterShip expects your endpoint to respond with an HTTP 2xx status code promptly. If your processing takes too long or returns a non-2xx response, AfterShip will treat the delivery as failed and begin retrying. Always acknowledge the webhook immediately, then process the tracking update asynchronously using a message queue or background job system.

Use the tracking ID for idempotency

Each tracking includes a unique id field and the event_id in the webhook payload. Use these to implement idempotent processing, since AfterShip may deliver the same event multiple times during retries:

async function processAfterShipWebhook(payload) {
  const idempotencyKey = `${payload.event_id}`;

  const exists = await redis.get(`processed:${idempotencyKey}`);
  if (exists) {
    console.log(`Event ${idempotencyKey} already processed, skipping`);
    return;
  }

  await handleTrackingUpdate(payload.msg);
  await redis.setex(`processed:${idempotencyKey}`, 604800, '1'); // 7-day TTL
}

Handle unstable enum values defensively

AfterShip's documentation explicitly warns that enum values like tag and subtag are not fixed and may be extended at any time. Avoid using strict enum validation or switch statements without a default case.

Build reconciliation as a safety net

Since AfterShip will permanently drop webhooks after 14 failed retry attempts, you should implement a periodic reconciliation job that polls the AfterShip Tracking API to catch any updates your webhook handler may have missed:

async function reconcileTrackings() {
  const activeTrackings = await db.getActiveTrackings();

  for (const tracking of activeTrackings) {
    const latest = await aftership.getTracking(tracking.id);
    if (latest.tag !== tracking.lastKnownStatus) {
      await handleTrackingUpdate(latest);
    }
  }
}

// Run every 30 minutes
setInterval(reconcileTrackings, 30 * 60 * 1000);

AfterShip webhook limitations and pain points

Permanent data loss after 14 failed retries

The Problem: If your endpoint is unreachable or returns a non-2xx response, AfterShip retries webhook delivery up to 14 times with exponential backoff (2^n × 30s). If all 14 attempts fail, the webhook is permanently discarded with no way to recover the event.

Why It Happens: AfterShip does not maintain a dead letter queue or event log for failed deliveries. Once the retry budget is exhausted, the tracking update is simply lost from the webhook pipeline.

Workarounds:

  • Implement a reconciliation job that periodically polls the AfterShip Tracking API to detect missed updates.
  • Ensure your endpoint has high availability with redundancy and failover.
  • Monitor your endpoint's uptime to catch outages before the retry window expires (the full retry cycle spans approximately 136 hours).

How Hookdeck Can Help: Hookdeck automatically stores all failed webhook deliveries in a dead letter queue with configurable retention, allowing you to inspect, debug, and replay them once your endpoint recovers. Hookdeck also supports up to 50 retry attempts over a longer window, significantly reducing the risk of permanent data loss.

No webhook delivery logs or replay capability

The Problem: AfterShip does not provide a browsable log of webhook deliveries or a mechanism to manually replay missed events. Developers have no visibility into whether webhooks were delivered successfully, what payloads were sent, or when failures occurred.

Why It Happens: AfterShip's webhook system is designed as a fire-and-forget delivery mechanism. There is no delivery audit trail exposed in the dashboard or API.

Workarounds:

  • Log all incoming webhooks on your end before processing them, so you have a record of what was received.
  • Build your own replay system by storing raw payloads and re-processing them when needed.
  • Use the Tracking API as the source of truth and treat webhooks as an optimization rather than the primary data source.

How Hookdeck Can Help: Hookdeck provides a full event log with searchable delivery history, payload inspection, and one-click replay for any event. You get complete visibility into every webhook AfterShip sends, including response codes, latency, and failure reasons.

Restrictive port number requirements

The Problem: AfterShip only delivers webhooks to endpoints on ports 80, 443, or 8080. Endpoints running on any other port will not receive webhooks.

Why It Happens: This is an intentional security restriction on AfterShip's side, limiting delivery to standard HTTP/HTTPS ports and one common alternative.

Workarounds:

  • Configure a reverse proxy (e.g., nginx) on port 80/443 to forward traffic to your application running on an internal port.
  • Use a tunneling service (e.g. Hookdeck CLI) during local development to expose a standard port.
  • Deploy behind a load balancer or API gateway that accepts traffic on standard ports.

How Hookdeck Can Help: Hookdeck provides a stable HTTPS endpoint on a standard port that receives webhooks from AfterShip, then forwards them to your service on any port or protocol, including localhost during development via the Hookdeck CLI.

Limited webhook URL slots

The Problem: AfterShip allows a maximum of 10 webhook URLs per account. For organizations that need to route tracking events to many different services, environments (staging, production, QA), or microservices, this cap can become a bottleneck.

Why It Happens: AfterShip imposes this limit at the account level, likely to manage their delivery infrastructure costs.

Workarounds:

  • Build a fan-out service that receives webhooks at a single URL and distributes them to multiple downstream consumers.
  • Use a message queue (e.g., Hookdeck, SQS, RabbitMQ) as a single webhook target and have multiple consumers subscribe.
  • Consolidate environments where possible.

How Hookdeck Can Help: Hookdeck supports unlimited destinations from a single webhook source. You can configure one AfterShip webhook URL pointing to Hookdeck and then fan out events to as many endpoints as needed, with independent retry policies, filters, and transformations for each destination.

Custom header cap of 5 per URL

The Problem: AfterShip limits you to 5 custom headers per webhook URL. For integrations that require multiple authentication tokens, routing metadata, or API-specific headers, this can be insufficient.

Why It Happens: This is an AfterShip platform limitation applied uniformly across all webhook URLs.

Workarounds:

  • Consolidate authentication into fewer headers where possible (e.g., combine metadata into a single JSON-encoded header).
  • Handle additional header requirements at your endpoint by looking up context based on the webhook URL or payload contents.

How Hookdeck Can Help: Hookdeck's transformations allow you to add, modify, or remove any number of headers before forwarding the webhook to your endpoint, removing the 5-header constraint entirely.

Frequent breaking changes from version migrations

The Problem: AfterShip releases new API and webhook versions roughly every six months, and older versions are eventually deprecated with a hard end-of-life date. Each migration can rename fields (e.g., expected_delivery to courier_estimated_delivery_date, checkpoint.zip to checkpoint.postal_code), remove headers (e.g., aftership-api-key to as-api-key), and change data formats.

Why It Happens: AfterShip actively evolves their data model to accommodate new carriers, AI-powered delivery predictions, and expanded tracking capabilities. The trade-off is a significant migration burden on consumers.

Workarounds:

  • Pin your webhook to a specific version and only migrate when approaching the end-of-life date.
  • Abstract your tracking data model behind an internal interface so field name changes only need updating in one place.
  • Monitor AfterShip's migration guides and webhook changelog for upcoming changes.
  • Use the concurrent version testing capability (different versions on different URLs) to validate before switching.

How Hookdeck Can Help: Hookdeck's transformations can normalize incoming webhook payloads into a consistent format regardless of the AfterShip webhook version. When AfterShip renames fields or changes structures, you update the transformation — not your application code.

High webhook volume with no built-in throttling

The Problem: For high-volume shippers tracking thousands of packages daily, AfterShip can generate an enormous volume of webhook calls — each tracking update for every checkpoint across every shipment. There's no built-in mechanism to throttle, batch, or filter events before delivery.

Why It Happens: AfterShip delivers a webhook for every tracking status change on every shipment. During peak shipping periods (e.g., Black Friday, holiday season), this can produce massive spikes in webhook traffic that overwhelm receiving systems.

Workarounds:

  • Implement a queuing layer (e.g., Hookdeck, AWS SQS, Google Pub/Sub) between your webhook endpoint and your processing logic.
  • Process webhook events in batches rather than individually.
  • Build concurrency controls to avoid overloading downstream databases and services.

How Hookdeck Can Help: Hookdeck provides automatic rate limiting and queuing for inbound webhooks, ensuring your endpoints receive events at a pace they can handle. You can configure delivery rates, apply filters to discard irrelevant events, and use transformations to reduce payload sizes before forwarding.

Testing AfterShip webhooks

Use the built-in test feature

The AfterShip dashboard includes a Send test webhook button that delivers a sample payload to your configured URL. However, be aware that:

  • Test payloads may differ from real tracking updates in structure and content.
  • The test validates connectivity but doesn't exercise your full processing pipeline.
  • Real payloads will vary significantly across different carriers and tracking scenarios.

Use a request inspector

Before building your handler, inspect real AfterShip payloads using a request inspection tool like Hookdeck Console:

  1. Create a temporary webhook URL.
  2. Configure it as one of your AfterShip webhook URLs.
  3. Create a test tracking or wait for a real tracking update.
  4. Inspect the payload structure, headers, and signature.

Validate across carriers

AfterShip normalizes tracking data from 1,200+ carriers, but payload contents can vary significantly. Test your integration with trackings from multiple carriers to ensure your handler accommodates differences in:

  • Checkpoint detail granularity (some carriers provide street-level locations, others only city/country).
  • Estimated delivery date formats and confidence levels.
  • Subtag values and status progression patterns.

Conclusion

AfterShip webhooks provide a real-time integration layer for one of the most comprehensive shipment tracking platforms available. The rich payload structure with normalized tracking statuses, checkpoint history, and estimated delivery dates enables sophisticated fulfillment automation and customer communication workflows.

However, limitations around permanent data loss after 14 retries, the absence of delivery logs and replay functionality, restrictive URL and header limits, frequent version migrations, and high-volume traffic spikes mean production deployments require careful defensive coding and infrastructure planning. Implementing proper signature verification, idempotent processing, asynchronous handling, and API-based reconciliation will address most common issues.

For teams shipping at moderate volumes with reliable infrastructure, AfterShip's built-in webhook system combined with proper error handling works well. For high-volume e-commerce operations, multi-destination routing, or mission-critical fulfillment workflows where delivery guarantees matter, webhook infrastructure like Hookdeck can address AfterShip's limitations by providing dead letter queues, payload transformation, automatic rate limiting, delivery logging, and comprehensive monitoring without modifying your AfterShip configuration.


Gareth Wilson

Gareth Wilson

Product Marketing

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