Delivery

Parcel

Parcel delivery provider — OAuth2 authentication, multi-region support, and real-time tracking.

The @commercejs/delivery-parcel package implements the DeliveryProvider interface for Parcel, a multi-region last-mile delivery service with OAuth2 authentication.

Installation

pnpm add @commercejs/delivery-parcel

Quick Start

import { ParcelDeliveryProvider } from '@commercejs/delivery-parcel'

const parcel = new ParcelDeliveryProvider({
  clientId: process.env.PARCEL_CLIENT_ID!,
  clientSecret: process.env.PARCEL_CLIENT_SECRET!,
  region: 'SA-riyadh',
})

// Estimate delivery fee
const estimate = await parcel.estimate({
  origin: {
    contactName: 'Store',
    contactPhone: '+966500000000',
    firstLine: 'Olaya St, Riyadh',
    latitude: 24.7136,
    longitude: 46.6753,
  },
  destination: {
    contactName: 'Customer',
    contactPhone: '+966512345678',
    firstLine: 'King Fahd Rd, Riyadh',
    latitude: 24.7743,
    longitude: 46.7386,
  },
})

Configuration

OptionTypeRequiredDescription
clientIdstringOAuth2 client ID
clientSecretstringOAuth2 client secret
webhookSecretstringSecret for webhook payload verification
regionstringRegion header (e.g., SA-riyadh, BH-manama)
baseUrlstringAPI base URL (default: https://api.tryparcel.com/api)
fetchFntypeof fetchCustom fetch function for testing

Authentication

Parcel uses OAuth2 client_credentials flow. The provider handles this automatically:

  1. Acquires an access token on the first API call
  2. Caches the token and reuses it for subsequent calls
  3. Refreshes 60 seconds before expiry
  4. Auto-retries with a fresh token on 401 responses
You never need to manage tokens manually — the provider handles the full OAuth2 lifecycle.

Methods

estimate

Estimates the delivery fee between two points:

const estimate = await parcel.estimate({
  origin: { latitude: 24.7136, longitude: 46.6753 },
  destination: { latitude: 24.7743, longitude: 46.7386 },
})

console.log(estimate.fee)              // 12.5
console.log(estimate.currency)         // 'SAR'
console.log(estimate.estimatedDuration) // 20 (minutes)
console.log(estimate.estimatedDistance) // 7000 (meters)

createDelivery

Creates a new delivery task:

const delivery = await parcel.createDelivery({
  origin: {
    contactName: 'Shop Manager',
    contactPhone: '+966500000000',
    firstLine: 'King Abdullah Road',
    latitude: 24.7136,
    longitude: 46.6753,
  },
  destination: {
    contactName: 'Ahmed',
    contactPhone: '+966512345678',
    firstLine: 'Al Malqa District',
    latitude: 24.8021,
    longitude: 46.6271,
    instructions: 'Leave at reception',
  },
  orderId: 'order_123',
  payment: { amount: 50, type: 'cash' },  // COD support
})
Set payment.type to 'cash' for cash-on-delivery (COD) orders. Any other value maps to 'prepaid'.

getDelivery

Retrieves the current state of a delivery:

const delivery = await parcel.getDelivery('TR-001')
console.log(delivery.status)      // 'in_transit'
console.log(delivery.trackingUrl) // 'https://track.tryparcel.com/TR-001'
console.log(delivery.driver)      // { name: 'Mohammed', phone: '...', latitude: ..., longitude: ... }

cancelDelivery

Cancels an active delivery:

const cancelled = await parcel.cancelDelivery('TR-001')
console.log(cancelled.status) // 'cancelled'

Status Mapping

Parcel task statuses are normalized to the DeliveryStatus union:

Parcel StatusDeliveryStatus
Unassignedpending
Acquiring Locationpending
Assignedassigned
In Progressin_transit
Completeddelivered
Successfuldelivered
Canceledcancelled
Location Inquiry Expiredfailed

Webhook Verification

Parcel embeds a WebhookSecret in the payload body. Configure the secret in the provider to enable verification:

const parcel = new ParcelDeliveryProvider({
  clientId: process.env.PARCEL_CLIENT_ID!,
  clientSecret: process.env.PARCEL_CLIENT_SECRET!,
  webhookSecret: process.env.PARCEL_WEBHOOK_SECRET!,
})

const event = await parcel.verifyWebhook(request.body, '')

console.log(event.type)       // 'delivery.updated' | 'delivery.location'
console.log(event.deliveryId) // 'TR-001'
console.log(event.status)     // 'delivered'
console.log(event.location)   // { latitude: 24.73, longitude: 46.69 }

Hook Types

Hook TypeEvent Type
taskUpdatedelivery.updated
driverLocationdelivery.location

Multi-Region Support

Parcel operates across multiple regions. Set the region option to target a specific city:

RegionCode
RiyadhSA-riyadh
JeddahSA-jeddah
ManamaBH-manama
The region is sent as a custom HTTP header with every API request.

Using with the Orchestrator

import { createCommerce } from '@commercejs/core'
import { ParcelDeliveryProvider } from '@commercejs/delivery-parcel'

const commerce = createCommerce({
  adapter,
  delivery: {
    parcel: new ParcelDeliveryProvider({
      clientId: process.env.PARCEL_CLIENT_ID!,
      clientSecret: process.env.PARCEL_CLIENT_SECRET!,
      region: 'SA-riyadh',
    }),
  },
  defaultDelivery: 'parcel',
})