AfterShip can tell your systems when a shipment changes state; Volanea can deliver the message that customer should receive. This guide explains how to send email from AfterShip using AfterShip’s outbound tracking webhooks and the Volanea REST API—without claiming there is a native marketplace app where none exists.

AfterShip Tracking has webhook notifications for tracking events. Volanea does not ship a native AfterShip app, marketplace listing, or one-click connector. The dependable integration pattern is therefore an adapter: AfterShip posts a signed shipment event to an endpoint you control, that endpoint verifies the request, decides whether an email is warranted, and makes an authenticated REST call to Volanea.

That extra hop is not needless complexity. It keeps the Volanea API key out of a browser and out of a carrier-tracking product’s event payload, gives you a place to prevent duplicate notices, and lets you make deliberate decisions about which delivery states should result in customer email.

What this integration does—and does not do

The integration has three separate responsibilities:

  1. AfterShip Tracking observes a shipment and emits a webhook when the tracking record is updated.
  2. Your webhook adapter validates the event, reads the recipient and shipment details, applies idempotency rules, and creates email content.
  3. Volanea accepts the resulting transactional email request and handles message sending and delivery infrastructure.

The concrete AfterShip trigger in this guide is the Tracking Updated webhook event, represented in the webhook body as tracking_update. This event is emitted when AfterShip updates the tracking record, including when its delivery tag moves to states such as InTransit, OutForDelivery, Delivered, Exception, or FailedAttempt.

A Tracking Updated event is not the same thing as “send the customer an email.” Carrier scans can be repeated, corrected, or arrive out of order. Your adapter should treat the tracking update as an input to a notification decision, rather than blindly sending one email for every inbound POST.

Also note the boundary of this setup: AfterShip’s webhook is an outbound HTTP notification. It is not a programmable Volanea action and it does not safely hold your Volanea credential. The adapter is required to transform the AfterShip-specific body into an email request and to preserve the secret on the server.

Why a webhook adapter is the right connection method

AfterShip supports webhooks for tracking activity. A webhook destination is an HTTPS URL that receives event data; it is not a general-purpose workflow runtime where you should put an email-provider secret or customer-facing template logic. A serverless function, container endpoint, or small application route is the natural bridge.

This architecture has useful operational properties:

  • Secret isolation: only the adapter can read the Volanea API key.
  • Signature verification: the adapter can reject requests that were not signed by AfterShip.
  • Idempotency: the adapter can recognize a tracking event it already handled.
  • Filtering: you can send on OutForDelivery and Delivered while ignoring routine location scans.
  • Observability: a structured log can connect an AfterShip tracking ID, a Volanea email ID, and a request outcome.
  • Template ownership: message rendering can live in version-controlled code rather than in a webhook receiver with limited transformation options.

You can host the adapter on a conventional Node server, AWS Lambda, Cloudflare Workers, Vercel Functions, Google Cloud Functions, or any runtime that can receive an HTTPS POST and make an outbound HTTPS request. The code below uses Node and Express because the raw-body handling and signature check are easy to inspect.

If you do not operate backend code, an automation platform can be used as middleware, provided it can receive AfterShip webhooks, keep secrets private, deduplicate events, and make a custom HTTP request. That is still a middleware integration—not a direct native AfterShip-to-Volanea connection. For production delivery notices, code you control is usually preferable because retries and duplicate scans need precise handling.

Configure the AfterShip tracking webhook

Create an HTTPS endpoint before configuring the webhook. It should accept POST requests, retain the unmodified request body long enough to verify the signature, and return a successful response promptly after it has durably accepted or rejected the work.

In AfterShip Tracking’s webhook settings, register your endpoint for the Tracking Updated event. AfterShip sends tracking information in a JSON request body and includes a signature header for webhook authentication. Do not rely on an IP allowlist alone, and do not treat the fact that a request reaches a public URL as proof that it came from AfterShip.

Use a dedicated endpoint, such as:

POST https://notifications.example.com/webhooks/aftership/tracking

Keep the endpoint narrowly scoped. It should not render a public page, accept browser form submissions, or share an authentication bypass with unrelated application routes. Terminate TLS, enforce a reasonable request-size limit, and return 405 Method Not Allowed for methods other than POST.

The event payload you should expect

AfterShip’s tracking webhook body has an event name and a msg object containing the tracking record. A Tracking Updated payload is shaped like this; individual fields can vary by carrier, shipment, account configuration, and the information supplied when the tracking was created:

{
  "event": "tracking_update",
  "msg": {
    "id": "5f4f1f0f4e6d2d0012345678",
    "tracking_number": "1Z999AA10123456784",
    "slug": "ups",
    "title": "Order #1042",
    "courier_name": "UPS",
    "active": true,
    "emails": ["customer@example.com"],
    "tag": "OutForDelivery",
    "subtag": "OutForDelivery_001",
    "subtag_message": "Out for delivery",
    "expected_delivery": "2026-09-30",
    "updated_at": "2026-09-29T08:32:10+00:00",
    "checkpoints": [
      {
        "checkpoint_time": "2026-09-29T08:30:00+00:00",
        "city": "Austin",
        "state": "TX",
        "country_iso3": "USA",
        "message": "Out for delivery",
        "tag": "OutForDelivery"
      }
    ],
    "custom_fields": {
      "order_id": "1042"
    }
  }
}

The important mapping is msg.emails[0] to the recipient, msg.title or msg.custom_fields.order_id to an order reference, msg.tracking_number to the tracking reference, and msg.tag to the notification state. The most recent checkpoint is usually the final entry in msg.checkpoints, but code should not assume a checkpoint list is present or nonempty.

Do not assume emails, title, expected_delivery, or custom fields will always exist. An email address appears only if it was associated with the tracking record. Custom fields exist only when your tracking-creation process adds them. The adapter must either use a reliable fallback—such as looking up the customer by order ID in your own database—or skip the send and log why it could not find a recipient.

Build the Volanea webhook adapter

The following Express route demonstrates the entire hop: preserve raw bytes, verify the AfterShip signature, require a Tracking Updated event, map fields, deduplicate, and call the Volanea email API. It intentionally sends only OutForDelivery and Delivered notifications. Add exception states only after you have agreed on customer-facing wording and support ownership.

import crypto from "node:crypto";
import express from "express";

const app = express();

// Keep the exact bytes: signature verification must happen before JSON parsing.
app.post(
  "/webhooks/aftership/tracking",
  express.raw({ type: "application/json", limit: "256kb" }),
  async (req, res) => {
    const signature = req.get("aftership-hmac-sha256");
    const expected = crypto
      .createHmac("sha256", process.env.AFTERSHIP_WEBHOOK_SECRET)
      .update(req.body)
      .digest("base64");

    if (
      !signature ||
      signature.length !== expected.length ||
      !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
    ) {
      return res.status(401).json({ error: "Invalid webhook signature" });
    }

    let payload;
    try {
      payload = JSON.parse(req.body.toString("utf8"));
    } catch {
      return res.status(400).json({ error: "Invalid JSON" });
    }

    if (payload.event !== "tracking_update") {
      return res.status(204).end();
    }

    const tracking = payload.msg ?? {};
    const recipient = tracking.emails?.[0];
    const status = tracking.tag;
    const orderId = tracking.custom_fields?.order_id ?? tracking.title ?? "your order";
    const trackingNumber = tracking.tracking_number;
    const allowedStates = new Set(["OutForDelivery", "Delivered"]);

    if (!allowedStates.has(status)) return res.status(204).end();
    if (!recipient || !tracking.id || !trackingNumber) {
      console.warn("AfterShip event missing required notification fields", {
        trackingId: tracking.id,
        hasRecipient: Boolean(recipient),
        status
      });
      return res.status(204).end();
    }

    // Replace these functions with Redis, Postgres, or another durable store.
    // The key identifies one notification type for one version of one tracking record.
    const idempotencyKey = `aftership:${tracking.id}:${status}:${tracking.updated_at}`;
    if (await alreadyProcessed(idempotencyKey)) return res.status(204).end();

    const subject = status === "Delivered"
      ? `Delivered: ${orderId}`
      : `Out for delivery: ${orderId}`;
    const statusText = status === "Delivered" ? "has been delivered" : "is out for delivery";

    const emailResponse = await fetch("https://api.volanea.com/v1/emails", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.VOLANEA_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey
      },
      body: JSON.stringify({
        from: "Shipping updates <shipping@updates.example.com>",
        to: [recipient],
        subject,
        text: `Your shipment for ${orderId} ${statusText}. Tracking number: ${trackingNumber}.`,
        html: `<p>Your shipment for <strong>${escapeHtml(orderId)}</strong> ${statusText}.</p><p>Tracking number: <strong>${escapeHtml(trackingNumber)}</strong></p>`,
        tags: ["aftership", status.toLowerCase()],
        metadata: {
          aftership_tracking_id: tracking.id,
          aftership_tracking_number: trackingNumber,
          aftership_status: status
        }
      })
    });

    if (!emailResponse.ok) {
      const detail = await emailResponse.text();
      console.error("Volanea send failed", emailResponse.status, detail);
      // Return 500 only if your idempotency design can safely retry this event.
      return res.status(502).json({ error: "Email provider request failed" });
    }

    await markProcessed(idempotencyKey);
    return res.status(202).json({ accepted: true });
  }
);

function escapeHtml(value) {
  return String(value).replace(/[&<>'"]/g, c => ({
    "&": "&amp;", "<": "&lt;", ">": "&gt;", "'": "&#39;", '"': "&quot;"
  }[c]));
}

async function alreadyProcessed(key) { /* query durable storage */ return false; }
async function markProcessed(key) { /* insert key atomically into durable storage */ }

app.listen(3000);

The example uses the Volanea REST email endpoint, a Bearer API key, and the usual transactional message fields: sender, recipients, subject, plain text, HTML, tags, and metadata. Consult the email API reference and setup guides before deploying to confirm the current endpoint version, supported optional fields, and response format for your account.

Field mapping, explained

The mapping deserves scrutiny because shipment data is operational data, not automatically customer-ready copy.

AfterShip fieldVolanea field or useReason
msg.emails[0]to[0]Recipient associated with the tracking record. Validate or look up a fallback if absent.
msg.title / msg.custom_fields.order_idsubject, text, htmlIdentifies the order without exposing unnecessary shipment details.
msg.tagconditional logic, subject, tagsDetermines whether the update merits a notification.
msg.tracking_numbertext, html, metadataLets customers and support identify the shipment.
msg.idmetadata and idempotency keyStable tracking-record identifier for troubleshooting.
msg.updated_atidempotency keyDistinguishes later legitimate updates from a retry of the same event.
msg.checkpointsoptional copy onlyUseful for a location or carrier message, but may be empty or inconsistent.

Do not use an unescaped carrier message directly in HTML. Carrier-provided text is external input. Escape it before interpolation, or use a template engine that escapes variables by default. Avoid including a full address, phone number, or other sensitive tracking fields unless there is a clear customer need and a documented privacy basis.

Keep Volanea credentials server-side

Set VOLANEA_API_KEY as an encrypted environment variable or secret in the service that runs the adapter. Set AFTERSHIP_WEBHOOK_SECRET there as well. Neither value belongs in frontend JavaScript, a mobile app, an email template, a public repository, a tracking URL, or an AfterShip custom field.

The Volanea key authorizes sending email from your account. If it is placed in client-visible configuration, anyone who inspects the application bundle, browser network traffic, or exposed configuration can potentially use it to send mail. That can create immediate abuse, cost, deliverability, and reputational consequences.

Use separate keys and sender identities for staging and production. Scope access as narrowly as Volanea supports, rotate a key when a person or system no longer needs it, and record which deployment owns each key. A secret manager is preferable to copying keys into deployment settings manually.

The AfterShip webhook secret has a different purpose: it lets your adapter authenticate the inbound request. Verify the HMAC against the raw request body before parsing or acting on the event. Constant-time comparison matters because ordinary string comparison can leak timing information in some contexts.

Choose notification rules customers will appreciate

The technically easiest rule is “email on every tracking update.” It is rarely the best customer experience. A parcel can generate numerous scans, and a customer who gets an email for every hub departure will tune out the one message that matters.

A practical starting policy is:

  • Send an out-for-delivery message when msg.tag is OutForDelivery.
  • Send a delivered confirmation when msg.tag is Delivered.
  • Create an internal support task, rather than an immediate customer email, for Exception until your operations team defines a response.
  • Treat FailedAttempt carefully; the carrier may later correct the status, and the customer may need more context than a generic template provides.
  • Do not send for every InTransit update unless your product promises milestone notifications and you have rate limits.

Use a per-shipment notification state in your database. For example, once out_for_delivery_sent_at is set, do not send it again merely because another Tracking Updated webhook arrives with the same tag. A later Delivered event remains eligible because it is a different state and customer message.

The template should include the shipment state, order reference, tracking number, and an accessible tracking link if your application has one. Plain-text content is not an afterthought: it is a useful fallback and makes the notification readable in clients that do not render HTML.

When this breaks: retries, timeouts, and incomplete data

A webhook integration must be designed for failure conditions, not just the happy-path demo. The riskiest failure is an ambiguous result: Volanea accepted an email, but your adapter timed out or crashed before recording that success. AfterShip may retry delivery because it did not receive a successful response, and the customer can receive a duplicate.

AfterShip retries can create duplicate sends

Webhook delivery systems retry unsuccessful or timed-out requests. They can also deliver an event more than once in distributed systems. Assume at-least-once delivery, not exactly-once delivery.

Use two safeguards together. First, store a durable idempotency record before or during processing with a unique constraint on a stable key. Second, pass the same idempotency key to Volanea when the API supports it. The sample key combines tracking ID, status, and update timestamp; your production key should be based on the exact event semantics you want to consider identical.

Do not mark an event processed before you know whether Volanea accepted it, unless your queue and recovery process can revisit pending work. A robust design stores states such as received, sending, sent, and failed, along with the Volanea response identifier. If a worker dies in sending, a scheduled recovery job can safely inspect the record and decide whether to retry.

Webhook timeouts need an acknowledgement strategy

Do not make the webhook request wait on slow template rendering, database contention, or a long downstream email call if your platform’s delivery timeout is tight. The strongest pattern is to validate the signature, write the event plus idempotency key to a durable queue or database, return a 2xx response, then send through Volanea asynchronously.

That pattern changes the meaning of the webhook response: 202 Accepted means your system accepted responsibility for the event, not that the email has already been delivered. Monitor the queue and retry worker separately. If you instead send inline, set a strict outbound timeout and make sure a timeout cannot lead to an untracked duplicate.

Some payload fields will be missing

A tracking record can lack emails because no recipient was attached when it was created. custom_fields may not be populated by your account or workflow. A carrier may not provide a meaningful expected delivery date, city, or checkpoint message. Treat all of these as optional.

Define fallbacks before launch. The best fallback for an absent emails array is usually an order lookup in your own commerce system using an order reference you stored when creating the tracking. If no safe recipient is available, skip the email, log the tracking ID and reason, and alert the operations owner if the rate rises. Never guess an address from a customer name or send to a shared mailbox merely to avoid a dropped notification.

Test the whole shipment-to-email path

Test with non-customer addresses and a verified non-production sender before enabling the webhook for live shipments. A unit test that only checks HTML rendering does not prove that signature validation, event filtering, and idempotency work together.

Your release checklist should include the following cases:

  1. A valid tracking_update with OutForDelivery sends one email with the correct recipient and order reference.
  2. The same exact webhook delivered twice produces one email, not two.
  3. A Delivered update after an out-for-delivery update sends a distinct message.
  4. An invalid aftership-hmac-sha256 header receives 401 and never reaches Volanea.
  5. A payload with no emails is logged and skipped without a provider call.
  6. A Volanea 4xx response is captured as a configuration or content failure; it should not be retried forever.
  7. A transient 5xx or network failure follows a bounded retry policy with the same idempotency key.
  8. HTML escaping prevents a malicious or malformed title from changing the email markup.

Use a dedicated test tracking record where possible, then inspect your application logs and Volanea message activity. Capture correlation values—AfterShip tracking ID, status, idempotency key, and the Volanea response identifier—rather than logging full recipient addresses or entire webhook bodies. Those values are enough for diagnosis while minimizing exposure of personal data.

Deliverability and sender identity still matter

An AfterShip event is often timely and highly relevant, which is good for engagement. It does not remove the usual obligations of transactional email. Send from a domain you control, authenticate that domain, use a clear sender name, and maintain a reply path or support route for customers who need help.

Keep shipping notifications recognizably transactional. Do not use a delivery confirmation as a place to add unrelated promotional content unless you have the appropriate permission and a clear separation in message purpose. A recipient who needs a package status update should not have to parse marketing copy to find it.

Use tags and metadata to measure the integration. Tags such as aftership, outfordelivery, and delivered make it easier to compare bounces, complaints, and delivery outcomes by notification type. Metadata containing the AfterShip tracking ID allows support to trace a specific event without making that ID part of the visible email content.

Operating the integration over time

Once the first version works, the next improvements are usually operational rather than visual. Add a dead-letter queue for events that repeatedly fail, dashboards for skipped sends and duplicate suppressions, and alerts for signature failures or a sudden increase in Volanea API errors.

Review your status policy when carriers or fulfillment processes change. A new carrier may use checkpoints differently; a marketplace may create tracking records without customer emails; an order-splitting change may mean one order now has multiple packages. Model notifications per tracking record or package, not only per order, or customers can receive misleading “delivered” messages when only part of an order arrived.

Keep your adapter small and explicit. It is tempting to turn it into a general automation engine, but a focused shipment-notification service is easier to test, secure, and audit. If you later add SMS, support tickets, or analytics events, give each downstream action its own idempotency and failure policy.

Conclusion

To send email from AfterShip with Volanea, use AfterShip’s Tracking Updated webhook as the trigger and a secure server-side adapter as the connection layer. Verify the webhook signature, require a real recipient and eligible shipment state, deduplicate aggressively, then map the tracking record into a Volanea transactional email request.

The key design decision is to treat carrier updates as potentially repeated operational events, not guaranteed one-time customer notifications. With durable idempotency, fast acknowledgement, protected credentials, and a conservative notification policy, the integration can deliver useful shipping messages without exposing secrets or flooding inboxes.

FAQ

Does Volanea have a native AfterShip integration?

No. Volanea does not provide a native AfterShip marketplace app or plugin. Use AfterShip’s outbound tracking webhook with a serverless function, application endpoint, or other secure middleware that calls the Volanea REST API.

What AfterShip event should trigger the email?

Use the Tracking Updated webhook event (tracking_update) and inspect msg.tag. Most teams begin with OutForDelivery and Delivered, then add exception handling only after defining the customer-support process.

Where should the Volanea API key be stored?

Store it as a server-side secret or encrypted environment variable in the middleware runtime. Never expose it in browser code, mobile applications, public repositories, URL parameters, or AfterShip tracking fields.

How do I stop duplicate shipment emails?

Assume AfterShip can retry webhook delivery. Store an idempotency key based on the tracking record and notification state in durable storage, enforce uniqueness, and use the same key on the Volanea send request when supported.

What if the AfterShip webhook has no customer email address?

Do not send to a guessed recipient. Look up the address from your own order system using a reliable order reference, or skip the event and log it for investigation. The emails field is not guaranteed on every tracking record.