> ## Documentation Index
> Fetch the complete documentation index at: https://docs.packageretriever.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracking & Webhooks

> Track shipments and receive label.created webhook notifications.

# Tracking & Webhooks

## Tracking

Register a tracking number to monitor shipment progress:

```typescript theme={null}
const tracker = await pr.trackers.create({
  tracking_number: '9400111899223408065744',
  carrier: 'USPS'
});
```

Poll for updates:

```typescript theme={null}
const status = await pr.trackers.get(tracker.id);
console.log(status.status); // 'in_transit', 'delivered', etc.
console.log(status.events); // Full chronological event history
```

### Tracking event statuses

| Status             | Meaning                                       |
| ------------------ | --------------------------------------------- |
| `pre_transit`      | Label created, not yet scanned                |
| `in_transit`       | Package picked up and moving                  |
| `out_for_delivery` | On vehicle for final delivery                 |
| `delivered`        | Confirmed delivered                           |
| `exception`        | Delivery issue (return to sender, held, etc.) |
| `unknown`          | Carrier status could not be normalized        |

Each event includes the raw carrier message in `status_detail` for cases where our normalized status isn't specific enough.

### Polling frequency

Our tracking data is updated every 4 hours via carrier API polling. For real-time needs, poll `GET /v1/trackers/:id` as frequently as your rate limit allows (300/min).

***

## Webhooks

Package Retriever pushes one webhook event: `label.created`. This fires immediately after a successful label purchase.

### Setup

Configure your webhook URL in the dashboard (Apps & Tools → Unleashed tab → Webhook section). You'll receive a signing secret at configuration time.

### Payload

```json theme={null}
{
  "event": "label.created",
  "data": {
    "id": "lbl_8k2m4n6p",
    "carrier": "USPS",
    "service": "Ground Advantage",
    "tracking_number": "9400111899223408065744",
    "rate_cents": 542,
    "label_url": "https://api.packageretriever.com/v1/labels/lbl_8k2m4n6p/download",
    "created_at": "2026-05-16T14:32:00Z"
  },
  "timestamp": "2026-05-16T14:32:01Z"
}
```

### Signature verification

Every webhook is signed with HMAC-SHA256. Verify the `PR-Signature` header:

**Node.js:**

```typescript theme={null}
import crypto from 'crypto';

function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return signature === `sha256=${expected}`;
}

// In your handler:
app.post('/webhooks/pr', (req, res) => {
  const payload = JSON.stringify(req.body);
  const signature = req.headers['pr-signature'] as string;

  if (!verifyWebhook(payload, signature, process.env.PR_WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const { event, data } = req.body;
  console.log(`Label created: ${data.tracking_number}`);

  res.status(200).send('OK');
});
```

**Python:**

```python theme={null}
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return signature == f"sha256={expected}"
```

### Retry schedule

If your endpoint doesn't return a 2xx response, we retry:

| Attempt | Delay      |
| ------- | ---------- |
| 1       | Immediate  |
| 2       | 5 minutes  |
| 3       | 30 minutes |
| 4       | 2 hours    |
| 5       | 24 hours   |

After 5 failed attempts, the delivery is marked as failed. You can view all delivery attempts in the dashboard (Apps & Tools → Unleashed → Webhook Deliveries).

### Delivery log

View your last 50 webhook deliveries via the dashboard or API:

```typescript theme={null}
const deliveries = await fetch('https://api.packageretriever.com/v1/webhooks/deliveries', {
  headers: { 'Authorization': `Bearer ${apiKey}` }
});
```

Each entry shows: status, response code, attempts, timestamps.

### Best practices

* Always return 200 quickly (process the payload asynchronously)
* Verify the signature before processing
* Handle duplicate deliveries idempotently (use `data.id` as deduplication key)
* If your endpoint is down, deliveries retry for up to 24 hours
