ShipEngine Tracking
Internal setup notes for ShipEngine package tracking and tracking update webhooks.
The ShipEngine integration starts package tracking when a purchase order enters the Shipped state and receives tracking status updates from ShipEngine webhooks.
The implementation lives in src/Modules/ShipEngine.
What The Integration Does
Tempr listens for purchase order state changes. When a PurchaseOrder transitions to Shipped, the ShipEngine listener:
- Reads tracking numbers from
purchase_orders.data.shipping. - Creates or reuses a
shipengine_packagesrow for each tracking number. - Calls ShipEngine's tracking subscription endpoint.
- Records
started_tracking_atwhen ShipEngine accepts the subscription. - Logs and skips packages that are missing tracking numbers or cannot be subscribed.
ShipEngine sends tracking events to:
POST /integrations/shipengine/webhooks/track
When the webhook is accepted, Tempr queues HandleTrackingWebhook, then updates the matching shipengine_packages row with the latest carrier status, tracking URL, delivery dates, event timestamp, and raw webhook payload.
Required Environment Variables
Add these values to .env.
SHIPENGINE_API_KEY=
SHIPENGINE_BASE_URL=https://api.shipengine.com
SHIPENGINE_TIMEOUT=10
SHIPENGINE_RETRY_TIMES=2
SHIPENGINE_RETRY_SLEEP=250
SHIPENGINE_QUEUE=default
SHIPENGINE_WEBHOOK_URL=https://your-public-host.example.com/integrations/shipengine/webhooks/track
SHIPENGINE_WEBHOOK_SECRET=
SHIPENGINE_WEBHOOK_SECRET_HEADER=X-ShipEngine-Webhook-Secret
Notes On Env Values
SHIPENGINE_API_KEY is required for all outbound ShipEngine calls.
SHIPENGINE_WEBHOOK_URL must be publicly reachable by ShipEngine over HTTPS. For local testing, use the active tunnel URL.
SHIPENGINE_WEBHOOK_SECRET is optional but recommended. When set, Tempr expects ShipEngine to send the configured secret in the header named by SHIPENGINE_WEBHOOK_SECRET_HEADER.
SHIPENGINE_QUEUE controls where webhook processing jobs are dispatched.
After changing .env, clear cached config:
php artisan optimize:clear
ShipEngine Webhook Setup
Create a ShipEngine webhook for the track event. The target URL should be:
https://your-public-host.example.com/integrations/shipengine/webhooks/track
If using webhook secret validation, add a custom ShipEngine webhook header:
| Header | Value |
|---|---|
X-ShipEngine-Webhook-Secret | The value of SHIPENGINE_WEBHOOK_SECRET |
The service also exposes a helper that can create a track webhook through the ShipEngine API:
php artisan tinker
app(\TemprApp\Server\Modules\ShipEngine\Services\ShipEngineService::class)
->createTrackWebhook();
Use the ShipEngine dashboard or listWebhooks() to confirm the webhook exists.
Database
Run migrations after deploying the module:
php artisan migrate
The integration stores package tracking state in shipengine_packages.
Important fields:
purchase_order_idcarrier_codetracking_numbertracking_urlstatus_codestatus_detail_codestatus_descriptionstatus_detail_descriptionestimated_delivery_dateactual_delivery_datestarted_tracking_atlast_event_atlast_payload
carrier_code and tracking_number are unique together.
Purchase Order Data Contract
The shipped action writes tracking details to:
{
"shipping": {
"tracking_numbers": ["1Z999AA10123456784"],
"method": "UPS",
"cost": "12.50"
}
}
The listener also accepts package-specific payloads at data.shipping.packages:
{
"shipping": {
"packages": [
{
"carrier_code": "ups",
"tracking_number": "1Z999AA10123456784"
}
]
}
}
Carrier labels entered by users are normalized for common carriers such as UPS, FedEx, USPS, and DHL.
Runtime Flow
Shipped State
- A user marks a purchase order as shipped.
- Tempr saves the shipping method, cost, and tracking numbers into the purchase order data payload.
- The purchase order transitions to
App\States\Orders\Shipped. StartTrackingPurchaseOrderPackagesreceives the state-change event.- ShipEngine tracking is started for each tracking number.
Tracking Webhook
- ShipEngine posts a tracking event to the public webhook URL.
ShipEngineTrackingWebhookControllervalidates the optional secret header.- Tempr accepts only
resource_type=API_TRACKpayloads. HandleTrackingWebhookupdates the matchingshipengine_packagesrow.
Verification
Confirm the route exists:
php artisan route:list --path=shipengine
Expected route:
POST integrations/shipengine/webhooks/track
Run focused tests:
php artisan test tests/Feature/ShipEngine/ShipEngineServiceTest.php
Troubleshooting
If tracking does not start, check:
- The purchase order has tracking numbers in
data.shipping.tracking_numbers. SHIPENGINE_API_KEYis present and config cache has been cleared.- The queue worker is running if
SHIPENGINE_QUEUEis not processed synchronously. - The carrier code is supported by ShipEngine for the tracking number.
If webhook updates do not arrive, check:
- ShipEngine has a
trackwebhook pointed at the current public URL. - The webhook secret header in ShipEngine matches Tempr's configured header and value.
- The payload has
resource_type=API_TRACK. shipengine_packagescontains the tracking number.