Track pre-booked shipments

Updated May 31, 20263 min read

You have a shipment that's already been booked with a carrier through another system. You want Carriyo to handle tracking, notifications, and the branded customer experience, without re-booking.

Scenario

Common cases:

  • Migration window. You're moving from another TMS to Carriyo. Existing in-flight shipments are pre-booked elsewhere.
  • Marketplace fulfillment. A marketplace handles fulfillment; you want unified branded tracking for your store.
  • Hybrid carrier setups. Most shipments go through Carriyo; some go direct to a carrier and need tracking unification.

Prerequisites

  • API credentials: see Getting started.
  • A shipment already booked elsewhere, with its carrier tracking number and the carrier's identity.
  • A configured carrier account that the input_carrier value resolves to. Carriyo uses it to follow the shipment with the carrier and receive status updates; without a matching account the shipment lands in error (see below). See Carrier configuration.

Register the shipment as pre-booked

Pre-booked shipments use the same POST /shipments endpoint, with pre_booked: true and the carrier's tracking details under pre_booking_info.

Pre-booked is lightweight: the only required fields are merchant, references, and pre_booking_info (with at least the carrier_tracking_no). You don't send pickup, parcels, or payment, Carriyo isn't booking anything. Include dropoff so customer notifications and the tracking page have a recipient, and add items if you want them shown on the tracking page or in reports.

curl -X POST 'https://api.carriyo.com/shipments' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'tenant-id: YOUR_TENANT_ID' \
  -H 'x-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "merchant": "ACME",
    "references": {
      "partner_order_reference": "YOUR_ORDER_REF",
      "partner_shipment_reference": "YOUR_ORDER_REF-1"
    },
    "pre_booked": true,
    "pre_booking_info": {
      "input_carrier": "ups",
      "carrier_tracking_no": "1Z999AA10123456784"
    },
    "dropoff": {
      "address1": "350 5th Avenue",
      "city": "New York",
      "state": "NY",
      "country": "US",
      "postcode": "10118",
      "contact_name": "Alex Chen",
      "contact_phone": "+12125550100",
      "contact_email": "alex@example.com"
    }
  }'

Unlike a normal booking, this resolves synchronously. Carriyo doesn't call the carrier, so there's no pending step. As long as input_carrier maps to one of your carrier accounts, the response comes back already booked, with the carrier account resolved and the tracking number you supplied echoed back:

{
  "shipment_id": "MPRBK7X2NVACD8",
  "references": {
    "partner_order_reference": "YOUR_ORDER_REF",
    "partner_shipment_reference": "YOUR_ORDER_REF-1"
  },
  "carrier_account": {
    "carrier": "UPS",
    "carrier_id": "c1053e4b-f2e7-4cee-917f-a390e73887a6",
    "carrier_account_name": "UPS US"
  },
  "pre_booking_info": {
    "input_carrier": "ups",
    "carrier_tracking_no": "1Z999AA10123456784",
    "carrier_tracking_url": "https://www.ups.com/track?tracknum=1Z999AA10123456784"
  },
  "post_shipping_info": {
    "status": "booked",
    "tracking_no": "1Z999AA10123456784",
    "key_milestones": {}
  },
  "...": "(other fields elided)"
}

status: "booked" here doesn't mean Carriyo booked anything; it confirms the shipment was registered against the matched carrier account and is ready for tracking. From this point Carriyo owns tracking the shipment with the carrier.

If the carrier doesn't map

input_carrier has to resolve to one of your configured carrier accounts. If it doesn't, Carriyo can't tell which account to track with, so the shipment is created in error instead of booked:

{
  "pre_booking_info": {
    "input_carrier": "USPS",
    "carrier_tracking_no": "94001111111111111497",
    "carrier_tracking_url": "https://tools.usps.com/go/TrackConfirmAction.action?tLabels=94001111111111111497"
  },
  "post_shipping_info": {
    "status": "error",
    "error_details": [
      {
        "level": "ERROR",
        "trigger": "BOOKING",
        "source": "CARRIYO",
        "type": "VALIDATION",
        "code": "invalid_input_carrier",
        "field": "input_carrier",
        "message": "Mapping for Input Carrier provided 'USPS' does not exist. Please pass the correct value or map this new value and reprocess booking again."
      },
      {
        "level": "ERROR",
        "trigger": "BOOKING",
        "source": "CARRIYO",
        "type": "VALIDATION",
        "code": "no_carrier_assigned",
        "field": "carrier",
        "message": "No carrier assigned. Please provide a valid carrier and reprocess booking again."
      }
    ],
    "key_milestones": {}
  },
  "...": "(other fields elided)"
}

Fix it by sending an input_carrier value that resolves to a configured carrier account (see Carrier configuration). Then reprocess the shipment with POST /shipments/{id}/reprocess.

How Carriyo tracks it

Once the pre-booked shipment is registered, Carriyo uses your carrier account credentials to follow the shipment the same way it would for a Carriyo-booked one. It polls the carrier's tracking API and accepts any callbacks the carrier sends to Carriyo's webhook URL. You don't feed updates in from your upstream system; Carriyo owns the tracking once the shipment is registered.

Whether those real-time callbacks need any setup depends on the carrier. For most it's automatic or a one-time configuration, nothing for you to do. Some carriers require a manual setup. Add Carriyo's callback URL to the carrier side (find it on your carrier account under Callback Settings) so the carrier can push status updates to Carriyo. Carriyo polls the carrier either way, so tracking still works without callbacks.

Branded customer experience

Once status updates start flowing, Carriyo's customer experience features work the same as for natively-booked shipments:

  • The customer's branded tracking page shows progress.
  • Notifications fire on status changes.
  • Webhook events go to your subscribers.
  • Reports include the shipment.

The customer can't tell the shipment was pre-booked elsewhere. That's the point.

What flows where

upstream system books with carrier
       │
       ▼
upstream system POSTs the resulting tracking_no to Carriyo
   (POST /shipments with pre_booked=true)
       │
       ▼
Carriyo matches input_carrier to a carrier account, records the
shipment as booked (synchronously), and takes over tracking
       │
       ▼
Carriyo pulls / receives status updates from the carrier
       │
       ▼
notifications, branded tracking, webhooks, reports

Pitfalls

  • input_carrier must resolve to a carrier account. It's how Carriyo decides which account to track with. If the value doesn't resolve, the shipment is created in error (invalid_input_carrier / no_carrier_assigned) rather than booked. Configure the account or correct input_carrier, then reprocess.
  • partner_shipment_reference is mandatory and tenant-unique. Reusing one returns a 400, not the existing shipment, so derive it from something stable on your side.
  • Out-of-order status events. Same as for natively-booked shipments, see Tracking events.