Skip to main content
This guide walks through the complete shipping workflow from order creation to delivery confirmation.

Workflow Overview

Step 1: Create Orders

When a customer places an order, create a parcel in CrossBorderly:
async function createShipment(order) {
  const response = await fetch('https://api.crbtrack.com/api/v1/orders/create', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.CROSSBORDERLY_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      idempotencyKey: `order-${order.id}`,
      externalOrderId: order.id,
      sender: {
        fullName: 'Your Company Name',
        phone: '+1 305-555-0123',
        address: {
          line1: '1234 Warehouse St',
          city: 'Miami',
          state: 'FL',
          zipCode: '33101',
          country: 'US'
        }
      },
      receiver: {
        fullName: order.customer.name,
        phone: order.customer.phone,
        email: order.customer.email,
        nationalId: order.customer.nationalId,
        address: {
          line1: order.shippingAddress.line1,
          city: order.shippingAddress.city,
          province: order.shippingAddress.province,
          zipCode: order.shippingAddress.zipCode,
          country: order.shippingAddress.country
        }
      },
      parcelDetails: {
        weightLbs: order.totalWeight,
        declaredValue: order.totalValue
      },
      products: order.items.map(item => ({
        description: item.name,
        quantity: item.quantity,
        unitFobValue: item.price,
        sku: item.sku
      }))
    })
  });

  const data = await response.json();
  
  // Store the tracking number in your database
  await db.orders.update(order.id, {
    trackingNumber: data.parcel.trackingNumber,
    labelUrl: data.parcel.labelUrl
  });
  
  return data;
}

Step 2: Print Labels

Download and print the shipping label:
async function downloadLabel(trackingNumber) {
  const response = await fetch(
    `https://api.crbtrack.com/api/v1/labels/${trackingNumber}/download`,
    {
      headers: { 'X-API-Key': process.env.CROSSBORDERLY_API_KEY }
    }
  );
  
  // For web: Create download link
  const blob = await response.blob();
  const url = URL.createObjectURL(blob);
  
  // For server: Save to file
  const buffer = await response.arrayBuffer();
  await fs.writeFile(`./labels/${trackingNumber}.pdf`, Buffer.from(buffer));
}

Step 3: Submit Manifest

Once you have a batch of parcels ready to ship, submit them as a manifest:
async function submitShipmentManifest(shipment) {
  const parcels = await db.parcels.findByShipment(shipment.id);
  
  const response = await fetch('https://api.crbtrack.com/api/v1/manifests/submit', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.CROSSBORDERLY_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      idempotencyKey: `manifest-${shipment.id}`,
      awbNumber: shipment.awbNumber,
      flightNumber: shipment.flightNumber,
      flightDate: shipment.flightDate,
      originAirport: 'MIA',
      destinationAirport: shipment.destination,
      carrier: shipment.carrier,
      country: shipment.country,
      parcels: parcels.map(p => ({
        trackingNumber: p.trackingNumber,
        bagCode: p.bagCode
      }))
    })
  });

  const data = await response.json();
  
  if (data.notFound?.length > 0) {
    console.warn('Some parcels not found:', data.notFound);
  }
  
  return data;
}

Step 4: Track Parcels

Configure webhooks to receive status updates automatically:
// Webhook endpoint
app.post('/webhooks/crossborderly', (req, res) => {
  const event = req.body;
  
  switch (event.event) {
    case 'parcel.status_changed':
      handleStatusChange(event.data);
      break;
    case 'parcel.delivered':
      handleDelivery(event.data);
      break;
    case 'parcel.exception':
      handleException(event.data);
      break;
  }
  
  res.status(200).send('OK');
});

async function handleStatusChange(data) {
  const order = await db.orders.findByTracking(data.trackingNumber);
  
  await db.orders.update(order.id, {
    shippingStatus: data.newStatus,
    lastUpdate: data.timestamp
  });
  
  // Notify customer
  await sendCustomerEmail(order.customer.email, {
    subject: 'Your order has been updated',
    status: data.newStatus,
    trackingUrl: `https://track.crbtrack.com/${data.trackingNumber}`
  });
}

Polling (Fallback)

For batch status checks:
async function syncTrackingStatus(trackingNumbers) {
  const response = await fetch('https://api.crbtrack.com/api/v1/tracking/batch', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.CROSSBORDERLY_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ trackingNumbers, includeHistory: false })
  });

  const data = await response.json();
  
  for (const result of data.results) {
    await db.orders.update({ trackingNumber: result.trackingNumber }, {
      shippingStatus: result.status
    });
  }
}

Step 5: Handle Exceptions

Build exception handling into your workflow:
async function handleShippingException(data) {
  const order = await db.orders.findByTracking(data.trackingNumber);
  
  switch (data.exceptionType) {
    case 'CUSTOMS_HOLD':
      // May need additional documentation
      await notifyOpsTeam(order, 'Customs hold - review required');
      break;
      
    case 'DELIVERY_FAILED':
      // Schedule retry or contact customer
      await scheduleDeliveryRetry(order);
      await sendDeliveryFailureEmail(order.customer.email);
      break;
      
    case 'ADDRESS_ISSUE':
      // Address correction needed
      await requestAddressCorrection(order);
      break;
      
    case 'RETURN_TO_SENDER':
      // Initiate refund process
      await initiateRefund(order);
      break;
  }
}

Best Practices

  • Create orders in batches during off-peak hours
  • Submit manifests when you have 50+ parcels ready
  • Use batch tracking instead of individual calls
  • Always use idempotency keys for POST requests
  • Implement retry logic with exponential backoff
  • Store raw API responses for troubleshooting
  • Cache label URLs (valid for 24 hours)
  • Use webhooks instead of polling when possible
  • Parallelize non-dependent API calls

Complete Integration Checklist

1

Account Setup

  • Get production API key
  • Configure webhook URL
  • Set up sandbox for testing
2

Order Integration

  • Map your order data to CrossBorderly format
  • Implement idempotency with order IDs
  • Handle validation errors
3

Label Management

  • Auto-download labels on order creation
  • Implement reprint functionality
  • Handle label format preferences (PDF/ZPL)
4

Manifest Workflow

  • Batch parcels by destination country
  • Submit manifests before carrier pickup
  • Handle partial success responses
5

Tracking

  • Configure webhook endpoints
  • Map status codes to your system
  • Build customer tracking page
6

Exception Handling

  • Monitor for delivery exceptions
  • Implement retry logic
  • Set up alerts for operations team