追跡例外の処理:開発者向けエッジケースガイド

理想的な世界では、すべての荷物が集荷から配達までスムーズに届けられるでしょう。しかし現実には、配送の5〜15%で例外が発生します。配達失敗、通関保留、荷物の破損、住所の問題など、さまざまなケースがあります。アプリケーションがこれらの例外をどのように処理するかによって、お客様が配送体験を信頼するか、サポート窓口に問い合わせが殺到するかが決まります。

よくある追跡例外の種類

例外のカテゴリを理解することで、それぞれに適切な処理を構築できます。

配達例外

{
  "status": "delivery_exception",
  "substatus": "failed_attempt",
  "description": "Delivery attempted - no one available to sign",
  "timestamp": "2026-01-18T14:30:00Z",
  "location": "Seoul, KR"
}

よくある配達例外には以下のものがあります。

  • 配達試行失敗 — 不在、営業時間外
  • アクセス不可 — セキュリティ付きマンション、施錠された建物
  • 受取拒否 — 受取人が荷物を拒否
  • 住所不備 — 住所が不完全または無効

通関例外

国際配送では通関に関連する保留が頻繁に発生します。

  • 書類不備 — コマーシャルインボイスや通関書類の不足
  • 検査 — 荷物が物理検査の対象に選定
  • 関税未払い — 受取人が輸入関税を支払う必要がある
  • 禁止品目 — 仕向国で制限されている内容物

輸送例外

  • 天候による遅延 — 悪天候による輸送への影響
  • 配送業者の遅延 — 仕分け施設の滞留、車両トラブル
  • 誤仕分け — 荷物が誤った施設に送られた
  • 破損 — 輸送中に荷物が損傷

例外処理システムの構築

ステップ1:分類と優先度付け

すべての例外に同じ対応が必要なわけではありません。優先度システムを構築しましょう。

const EXCEPTION_PRIORITY = {
  // Critical — requires immediate customer notification
  'damaged': 'critical',
  'lost': 'critical',
  'refused': 'critical',

  // High — customer action may be needed
  'customs_hold': 'high',
  'duties_pending': 'high',
  'incorrect_address': 'high',
  'documentation_required': 'high',

  // Medium — informational, carrier will retry
  'failed_attempt': 'medium',
  'access_issue': 'medium',
  'weather_delay': 'medium',

  // Low — temporary, usually resolves automatically
  'carrier_delay': 'low',
  'missort': 'low',
};

function handleException(event) {
  const priority = EXCEPTION_PRIORITY[event.substatus] || 'medium';

  switch (priority) {
    case 'critical':
      notifyCustomerImmediately(event);
      createSupportTicket(event);
      break;
    case 'high':
      notifyCustomerWithAction(event);
      break;
    case 'medium':
      updateTrackingPage(event);
      break;
    case 'low':
      logAndMonitor(event);
      break;
  }
}

ステップ2:お客様向けのわかりやすいメッセージ

技術的な追跡ステータスはお客様を混乱させます。明確で実行可能なメッセージに変換しましょう。

const CUSTOMER_MESSAGES = {
  'failed_attempt': {
    title: 'Delivery attempted',
    message: 'The carrier tried to deliver your package but was unable to. They will try again on the next business day.',
    action: null,
  },
  'customs_hold': {
    title: 'Package in customs',
    message: 'Your package is being processed by customs. This typically takes 1-5 business days.',
    action: null,
  },
  'duties_pending': {
    title: 'Action required: Import duties',
    message: 'Your package requires import duty payment before it can be released from customs.',
    action: {
      label: 'Pay duties',
      url: '/shipments/{trackingNumber}/duties',
    },
  },
  'incorrect_address': {
    title: 'Address issue',
    message: 'The carrier was unable to deliver due to an address issue. Please verify your shipping address.',
    action: {
      label: 'Update address',
      url: '/shipments/{trackingNumber}/address',
    },
  },
};

ステップ3:自動復旧ワークフロー

一部の例外は自動的に解決できます。

async function attemptAutoRecovery(shipment, exception) {
  switch (exception.substatus) {
    case 'failed_attempt': {
      const attempts = await getDeliveryAttempts(shipment.trackingNumber);
      if (attempts.length >= 3) {
        // After 3 failed attempts, offer pickup or redirect
        await notifyCustomer(shipment, {
          message: 'Multiple delivery attempts failed',
          options: ['Schedule redelivery', 'Pick up at facility', 'Redirect to new address'],
        });
      }
      // Otherwise, carrier will retry automatically
      break;
    }

    case 'incorrect_address': {
      // Check if we have an alternative address on file
      const altAddress = await getAlternativeAddress(shipment.recipientId);
      if (altAddress) {
        await requestAddressCorrection(shipment, altAddress);
      } else {
        await requestCustomerAddressUpdate(shipment);
      }
      break;
    }
  }
}

例外率のモニタリング

例外のパターンを追跡して、組織的な問題を特定しましょう。

// Daily exception rate monitoring
async function generateExceptionReport() {
  const today = new Date();
  const shipments = await getShipmentsForDate(today);

  const report = {
    total: shipments.length,
    exceptions: {},
    carrierBreakdown: {},
  };

  for (const shipment of shipments) {
    if (shipment.hasException) {
      const type = shipment.exceptionType;
      report.exceptions[type] = (report.exceptions[type] || 0) + 1;

      const carrier = shipment.carrier;
      if (!report.carrierBreakdown[carrier]) {
        report.carrierBreakdown[carrier] = { total: 0, exceptions: 0 };
      }
      report.carrierBreakdown[carrier].exceptions++;
    }
  }

  return report;
}

アラートの設定

例外率のアラートしきい値を定義します。

例外の種類警告しきい値重大しきい値
配達失敗> 10%> 20%
通関保留> 15%(国際)> 30%(国際)
破損> 1%> 3%
紛失> 0.5%> 1%
住所不備> 5%> 10%

Webhookベースの例外処理

WhereParcelのWebhookを使用して、リアルタイムで例外イベントを受信します。

// Webhook handler for tracking exceptions
app.post('/webhooks/tracking', async (req, res) => {
  const event = req.body;

  if (event.type === 'tracking.updated') {
    const { status, substatus } = event.data.latestEvent;

    if (isException(status)) {
      await handleException(event.data);

      // Log for analytics
      await logException({
        trackingNumber: event.data.trackingNumber,
        carrier: event.data.carrier,
        exceptionType: substatus,
        timestamp: event.data.latestEvent.timestamp,
      });
    }
  }

  res.status(200).json({ received: true });
});

ベストプラクティスまとめ

  1. 重要度別に例外を分類する — すべての例外に同じレベルの対応が必要なわけではありません
  2. お客様にわかりやすい言葉を使う — 技術的なステータスを明確なメッセージに変換しましょう
  3. 実行可能な次のステップを提示する — お客様ができることがあれば伝えましょう
  4. 可能な限り自動化する — よくある例外に対してワークフローを設定しましょう
  5. 例外率をモニタリングする — 配送業者やルートごとのパターンを追跡し、組織的な問題を早期に発見しましょう
  6. 解決までのSLAを設定する — 重大な例外は数日ではなく数時間以内に対処すべきです
  7. ポーリングではなくWebhookを使用する — リアルタイム通知により、タイムリーな例外処理が可能になります

堅牢な例外処理を構築することが、フラストレーションのたまる配送体験と信頼できる配送体験を分ける鍵です。WhereParcelの500以上の配送業者にわたる標準化された例外ステータスにより、このシステムを一度構築すれば、どの配送業者が関わっても一貫した例外処理が可能になります。

Webhookの設定について詳しくは、Webhook ベストプラクティスガイドをご覧ください。