API Documentation

Integrate Weels delivery into your platform. Create shipments, check rates, and manage deliveries programmatically.

Authentication

All API requests require an API token. You can find your token in the Developer tab of your Organization settings.

Include your token in one of two ways:

X-API-Key header
X-API-Key: your_api_token_here
Authorization header (Bearer token)
Authorization: Bearer your_api_token_here
Your API token identifies your organization. All shipments created via the API are billed to your organization's account.

Test Mode (Sandbox)

Test mode lets you develop and test your integration against the Weels API using real validation, real zone data, and real pricing — without dispatching shipments or being charged. It uses your same API token and the same endpoints. Shipments are stored in a separate test database so they never mix with live data.

This is useful for:

  • Realistic development — rates, validation errors, and labels all behave exactly like production. The only things skipped are dispatch, credit charges, and customer notifications.
  • Workflow validation — confirm that your system correctly handles success responses, parses tracking URLs, decodes labels, and processes errors before going live.
  • Team onboarding — let new developers experiment with the API freely without risk.

Enabling test mode

Toggle test mode on or off from the Developer tab in your Organization settings. When enabled, every API response will include a "test_mode": true field so your code can distinguish sandbox responses from live ones.

Remember to disable test mode before going live. It applies to all API calls made with your organization’s token. Shipments created from the Weels web dashboard are not affected.

Sandbox behaviour by endpoint

EndpointSandbox Behaviour
/api/shipment/rates Returns real zone rates with your organization’s discounts applied. Zone availability is checked against live data. Response includes "test_mode": true.
/api/shipment/create Runs full validation and real pricing. The shipment is saved to test_tasks (not production). Real shipping labels are generated. No credit is deducted, no dispatch occurs, and no SMS is sent.
/api/shipment/void Voids the test shipment in test_tasks. No credit refund or dispatch cancellation occurs.
/api/shipment/label Returns a real shipping label generated from the test shipment data. All formats (PDF, ZPL, base64) are supported.

Example response

{
  "success": true,
  "test_mode": true,
  "task_id": 47,
  "total_price": 10.15,
  "tracking_url": "https://www.weels.ca/track/TEST-A1B2C3D4",
  "label_pdf": "JVBERi0xLjcK... (base64-encoded PDF)",
  "label_zpl": "^XA\\n^PW812\\n... (ZPL code)",
  "message": "Test mode: shipment created in test_tasks. No credit charged, no dispatch."
}
Check for "test_mode": true in responses to add conditional logic in your integration — for example, showing a sandbox banner in your UI or logging test shipments separately.

Quick Start Guide

Ship your first package in minutes. Weels supports two shipping modes — choose the one that fits your needs, then follow the steps below.

Weels Driver Delivery

Same-day / next-day local delivery within Weels service zones. A Weels driver picks up from you and delivers to your customer.

Carrier Shipping

Canada Post & UPS for out-of-zone, long-distance, or international shipments. Labels are generated automatically on creation.

Minimal Example — Create & Label

This example walks through the full flow: check rates, create a shipment, and retrieve the label.

# 1. Check rates
curl -X POST https://www.weels.ca/api/shipment/rates \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"postal":"M5V3L9","packages":[{"weight":2,"length":10,"width":8,"height":4,"qty":1}]}'

# 2. Create shipment (use a service code from the rates response)
curl -X POST https://www.weels.ca/api/shipment/create \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "destination_name":"Jane Doe",
    "destination_phone":"4165551234",
    "destination_address":"100 Queen St W",
    "destination_city":"Toronto",
    "destination_province":"ON",
    "destination_postal":"M5H2N2",
    "destination_country":"CA",
    "packages":[{"weight":2,"length":10,"width":8,"height":4,"qty":1}],
    "shipping_method":"canada_post",
    "carrier_service_code":"DOM.EP"
  }'

# 3. Get label (use task_id from create response)
curl "https://www.weels.ca/api/shipment/label?task_id=12345&format=pdf" \
  -H "Authorization: Bearer YOUR_TOKEN"
Enable Test Mode first to try the full flow without being charged. All endpoints behave identically in sandbox — the only difference is no dispatch or billing occurs.
POST https://www.weels.ca/api/shipment/rates

Check if delivery is available at a postal code and get pricing. Returns Weels rates for in-zone destinations and, when carrier shipping is enabled, carrier rates from Canada Post and UPS.

Carrier Shipping: When enabled for your organization, this endpoint returns carrier rates alongside Weels rates. To receive carrier rates, include package dimensions (weight, width, length, height). For out-of-zone or international destinations where Weels delivery is not available, carrier rates provide an alternative. To create a carrier shipment, pass shipping_method and carrier_service_code to the Create Shipment endpoint. International shipments (destination_country other than CA) require a customs declaration. A default 11 AM ET pickup cutoff applies — delivery estimates are automatically adjusted for orders placed after cutoff.

Request Fields

FieldTypeRequiredOptionsDescription
postalstringYes Destination postal/ZIP code. Spaces are stripped automatically.
destination_countrystringNo Default: CA 2-letter ISO country code. Non-CA destinations skip the Weels zone lookup and return only carrier rates.
age_verificationintegerNo 0 18 19 21 Minimum age the recipient must verify upon delivery. 0 = none (default). Any other value adds a $1.00/pkg surcharge.
signaturebooleanNo true false Require the recipient's signature upon delivery. Default: false. Adds a $1.00/pkg surcharge when enabled.
identity_verificationbooleanNo true false Require the driver to scan the recipient’s government ID at the door. Name on ID must match the delivery. Default: false. Adds a $5.00/pkg surcharge. Weels only — carrier rates are excluded when enabled.
fragilebooleanNo true false Flag the shipment for special handling of delicate/perishable items. A visible *** FRAGILE *** tag is printed on the label. Default: false. Adds a $1.00/pkg surcharge. Weels only — carrier rates are excluded when enabled.
Packages — repeat with [0], [1], [2]... for multiple parcels
packages[n][qty]integerNo Default: 1 Quantity of identical items in this parcel. Combined total across all parcels determines the volume discount tier (5% for 2, 10% for 3, 15% for 4+).
packages[n][weight]decimalNo Parcel weight in lbs. Required for carrier rates.
packages[n][width]decimalNo Parcel width in inches. Required for carrier rates.
packages[n][length]decimalNo Parcel length in inches. Required for carrier rates.
packages[n][height]decimalNo Parcel height in inches. Required for carrier rates.
Only qty is needed for Weels rate calculation. To also receive carrier rates (Canada Post, UPS), include weight, width, length, and height for at least the first package. Carrier rates are only returned when your organization has carrier shipping enabled. Note: identity_verification and fragile are Weels-only features — when either is enabled, carrier rates are suppressed and only Weels rates are returned.

Example Request

curl -X POST https://www.weels.ca/api/shipment/rates \
  -H "X-API-Key: your_api_token_here" \
  -d "postal=L6A1G2" \
  -d "packages[0][qty]=1" \
  -d "packages[0][weight]=2.5" \
  -d "packages[0][width]=12" \
  -d "packages[0][length]=10" \
  -d "packages[0][height]=6" \
  -d "signature=true"

Success Response (In-Zone)

{
  "available": true,
  "city": "Maple",
  "province": "ON",
  "delivery_date": "2026-03-02",
  "qty": 1,
  "rate": {
    "base_rate": 8.99,
    "surcharges": 1.00,
    "discount_pct": 10.00,
    "discount_amount": 1.00,
    "subtotal": 8.99,
    "tax_hst": 1.17,
    "total": 10.16
  },
  "carrier_rates": [
    {
      "carrier": "canada_post",
      "service_code": "DOM.EP",
      "service_name": "Canada Post Expedited",
      "estimated_days": 2,
      "delivery_date": "2026-03-07",
      "tracked": true,
      "carrier_cost": 9.27,
      "subtotal": 11.12,
      "tax_hst": 1.45,
      "total": 12.57
    },
    {
      "carrier": "ups",
      "service_code": "11",
      "service_name": "UPS Standard",
      "estimated_days": 1,
      "delivery_date": "2026-03-06",
      "tracked": true,
      "carrier_cost": 12.50,
      "subtotal": 15.00,
      "tax_hst": 1.95,
      "total": 16.95
    }
  ]
}
carrier_rates is an empty array when carrier shipping is not enabled for your organization or no package dimensions are provided. The delivery_date reflects an 11 AM ET pickup cutoff — orders placed after 11 AM show the next available date.

Response (Out-of-Zone with Carrier Rates)

{
  "available": false,
  "postal": "V5K",
  "carrier_rates": [
    {
      "carrier": "canada_post",
      "service_code": "DOM.XP",
      "service_name": "Canada Post Xpresspost",
      "estimated_days": 3,
      "delivery_date": "2026-03-08",
      "tracked": true,
      "carrier_cost": 14.85,
      "subtotal": 17.82,
      "tax_hst": 2.32,
      "total": 20.14
    }
  ]
}

Response (Not Available)

{
  "available": false,
  "postal": "V5K"
}

Response Fields

FieldTypeDescription
availablebooleantrue if the postal code is within the Weels delivery zone.
citystringDelivery city (only when available is true).
provincestringDelivery province (only when available is true).
delivery_datestringEarliest available Weels delivery date (YYYY-MM-DD). Accounts for the 11 AM ET cutoff, allowed delivery days, and blackout dates.
qtyintegerTotal parcel quantity used for pricing.
rateobjectWeels pricing breakdown (only when available is true).
rate.base_ratedecimalZone base rate × quantity (before discounts).
rate.surchargesdecimalTotal surcharges (age verification, signature, etc.) × quantity.
rate.discount_pctdecimalCombined discount percentage (includes volume and organization discounts).
rate.discount_amountdecimalTotal dollar amount saved from all discounts.
rate.subtotaldecimalAmount after all discounts (before tax).
rate.tax_hstdecimalHST (13%) on the subtotal.
rate.totaldecimalFinal price including tax.
carrier_rates[] — array of carrier shipping options (requires package dimensions)
carrierstringCarrier identifier: canada_post or ups.
service_codestringCarrier service code. Pass this as carrier_service_code when creating a carrier shipment.
service_namestringHuman-readable service name (e.g. “Canada Post Expedited”, “UPS Standard”).
estimated_daysintegerEstimated transit days from the carrier.
delivery_datestringEstimated delivery date from the carrier (YYYY-MM-DD). Adjusted for the 11 AM ET cutoff.
trackedbooleanWhether the shipment includes tracking.
subtotaldecimalRate after markup (pre-tax).
tax_hstdecimalHST on the subtotal.
totaldecimalTotal price including tax. This is the amount that will be charged.
Prev: Check Rates Next: Get Label Related: Void Shipment
POST https://www.weels.ca/api/shipment/create

Create a new shipment. The recipient address is automatically verified. If address verification fails, the request is rejected with a descriptive error.

Request Fields

FieldTypeRequiredOptionsDescription
Sender (Origin)
origin_fnamestringYesSender first name
origin_lnamestringYesSender last name
origin_companystringNoSender company or business name
origin_phonestringNoSender phone number (digits only, e.g. 4165551234)
origin_emailstringNoSender email address
origin_address1stringYesSender street address (e.g. 100 King St W)
origin_address2stringNoSender unit, suite, or apartment number
origin_citystringYesSender city
origin_provincestringNoDefault: ONSender province (2-letter code)
origin_postalcodestringNoSender postal code
origin_countrystringNoDefault: CASender country code (2-letter ISO)
Recipient (Destination)
destination_fnamestringYesRecipient first name
destination_lnamestringYesRecipient last name
destination_companystringNoRecipient company or business name
destination_phonestringYesRecipient phone number (digits only, e.g. 4165551234)
destination_emailstringYesRecipient email address (used for delivery notifications)
destination_address1stringYesRecipient street address. Verified automatically for accuracy.
destination_address2stringNoRecipient unit, suite, or apartment number
destination_citystringYesRecipient city
destination_provincestringNoDefault: ONRecipient province (2-letter code)
destination_postalcodestringYesRecipient postal/ZIP code. Must be within Weels delivery zones for Weels shipments; any valid postal for carrier shipments.
destination_countrystringNoDefault: CARecipient country code (2-letter ISO). Non-CA destinations require carrier shipping and customs declaration.
Package Details — repeat with [0], [1], [2]... for multiple parcels
packages[n][qty]integerNoDefault: 1Quantity of identical items in this parcel. Combined total across all parcels determines volume discount tier.
packages[n][weight]decimalYesWeight of the parcel in lbs (e.g. 2.5)
packages[n][width]decimalYesWidth of the parcel in inches
packages[n][length]decimalYesLength of the parcel in inches
packages[n][height]decimalYesHeight of the parcel in inches
Shipment Options
referencestringNoMax 100 charsYour internal reference or tracking number. Displayed on the shipment label and included in webhooks.
order_numstringNoMax 100 charsYour order ID from your system (e.g. Shopify order number).
notesstringNoMax 500 charsSpecial delivery instructions for the courier (e.g. "Leave at side door", "Call on arrival").
delivery_datestringYesFormat: YYYY-MM-DDRequested delivery date. Cannot be in the past. Automatically adjusted forward if past the 11 AM ET cutoff, or if the date falls on a non-delivery day or blackout.
age_verificationintegerNo0 18 19 21Minimum age the recipient must verify at the door. 0 = none (default). Adds a $1.00/pkg surcharge when set.
signature_requiredbooleanNotrue falseRequire the recipient's signature on delivery. Default: false. Adds a $1.00/pkg surcharge when enabled.
identity_verificationbooleanNotrue falseRequire the driver to scan the recipient’s government ID. Name on the ID must match the name on the delivery — if verification fails, the delivery cannot be completed. Default: false. Adds a $5.00/pkg surcharge. Weels only.
fragilebooleanNotrue falseFlag the shipment for special handling. A prominent *** FRAGILE *** tag is printed on the shipping label. Default: false. Adds a $1.00/pkg surcharge. Weels only.
Carrier Shipping — for Canada Post / UPS shipments (optional)
shipping_methodstringNoweels canada_post upsShipping carrier. Default: weels. Carrier shipping must be enabled for your organization.
carrier_service_codestringConditionalRequired when shipping_method is canada_post or ups. Use the service_code value from the Check Rates carrier_rates response.
label_sizestringNo4x6 8.5x11Label format for carrier shipments. Default: 4x6 (thermal).
Customs Declarationrequired for international (destination_countryCA) carrier shipments
customs_items[n][description]stringYes*Max 45 charsDescription of the item (e.g. “Cotton T-Shirt”). At least one item is required for international shipments.
customs_items[n][quantity]integerYes*Number of units of this item.
customs_items[n][unit_value]decimalYes*Value per unit in the specified currency.
customs_items[n][hs_tariff_code]stringNoHarmonized System tariff code for customs classification.
customs_items[n][origin_country]stringNoDefault: CACountry of origin for the goods (2-letter ISO).
customs_currencystringNoDefault: CADCurrency for customs values.
customs_reasonstringNoDefault: SOGReason for export. SOG = Sale of Goods.
You can send multiple parcels by incrementing the array index: packages[0][...], packages[1][...], etc. Each parcel can have its own quantity and dimensions. Note: identity_verification and fragile require shipping_method to be weels (or omitted). These options are not compatible with carrier shipments.

Example Request

curl -X POST https://www.weels.ca/api/shipment/create \
  -H "X-API-Key: your_api_token_here" \
  -d "origin_fname=John" \
  -d "origin_lname=Doe" \
  -d "origin_address1=100 King St W" \
  -d "origin_city=Toronto" \
  -d "destination_fname=Jane" \
  -d "destination_lname=Smith" \
  -d "destination_phone=4165551234" \
  -d "[email protected]" \
  -d "destination_address1=30 Pamela Crt" \
  -d "destination_city=Maple" \
  -d "destination_postalcode=L6A1G2" \
  -d "packages[0][qty]=1" \
  -d "packages[0][weight]=2.5" \
  -d "packages[0][width]=12" \
  -d "packages[0][length]=10" \
  -d "packages[0][height]=6" \
  -d "delivery_date=2026-02-15" \
  -d "order_num=ORD-12345" \
  -d "reference=REF-001"

Success Response — Weels Shipment 200

{
  "success": true,
  "task_id": 142,
  "total_price": 10.15,
  "tracking_url": "https://www.weels.ca/track/abc123def",
  "label_pdf": "JVBERi0xLjcK... (base64-encoded PDF)",
  "label_zpl": "^XA\n^PW812\n... (ZPL code)",
  "message": "Shipment created successfully."
}

Success Response — Carrier Shipment 200

{
  "success": true,
  "task_id": 30000712,
  "total_price": 16.95,
  "tracking_url": "https://www.ups.com/track?tracknum=1Z...",
  "tracking_number": "1Z999AA10123456784",
  "carrier": "ups",
  "service_name": "UPS Standard",
  "label_pdf": "JVBERi0xLjcK... (base64-encoded PDF)",
  "label_zpl": "",
  "message": "Shipment created via UPS Standard."
}
The response includes label_pdf (base64-encoded PDF). For Weels shipments, label_zpl is also provided for thermal printers. For carrier shipments, tracking_number, carrier, and service_name are included. Decode the PDF with base64 to save or print it. You can also retrieve labels later via the Get Label endpoint.

Error Responses

400 Validation Error

{
  "error": "Recipient first name is required. Recipient postal code is required."
}

400 Address Verification Failed

{
  "code": "address_verification_failed",
  "error": "The postal code doesn't match the address. Expected: L6A1G3.",
  "details": ["The postal code doesn't match the address. Expected: L6A1G3."]
}

402 Insufficient Credit (prepay accounts only)

{
  "code": "insufficient_credit",
  "error": "Insufficient balance.",
  "balance": 5.00,
  "required": 10.15,
  "shortfall": 5.15
}
POST https://www.weels.ca/api/shipment/void

Cancel a pending shipment or pickup request that has not yet been picked up. Only shipments with a “Pending” status can be voided. For prepay accounts, the cost is refunded to your credit balance. This endpoint works for both deliveries and scheduled pickups.

Request Fields

FieldTypeRequiredOptionsDescription
task_idintegerYesThe Weels shipment ID to void. Returned as task_id in the Create Shipment response.

Example Request

curl -X POST https://www.weels.ca/api/shipment/void \
  -H "X-API-Key: your_api_token_here" \
  -d "task_id=142"

Success Response 200

{
  "success": true,
  "message": "Shipment #142 has been voided.",
  "refunded": true,
  "refund_amount": 10.15
}

Error Responses

400 Not Voidable

{
  "error": "This shipment cannot be voided. Only pending shipments can be voided."
}

404 Not Found

{
  "error": "Shipment not found."
}
GET https://www.weels.ca/api/shipment/label

Retrieve a shipping label for a previously created shipment. Available as a PDF (for standard printers) or ZPL (for Zebra thermal printers).

Request Fields

FieldTypeRequiredOptionsDescription
task_idintegerYesThe Weels shipment ID.
formatstringNopdf zpl pdf_base64Label format. pdf (default) streams the PDF inline. zpl returns raw ZPL text. pdf_base64 returns JSON with base64-encoded PDF.

Example Request (PDF)

curl -H "X-API-Key: your_api_token_here" \
  "https://www.weels.ca/api/shipment/label?task_id=142&format=pdf" \
  --output label-142.pdf

Example Request (ZPL)

curl -H "X-API-Key: your_api_token_here" \
  "https://www.weels.ca/api/shipment/label?task_id=142&format=zpl"

PDF Response

Returns the raw PDF binary with Content-Type: application/pdf. Open directly in a browser or save to file.

ZPL Response

Returns raw ZPL text with Content-Type: text/plain. Send directly to a Zebra thermal printer.

Base64 JSON Response

{
  "success": true,
  "task_id": 142,
  "label_pdf": "JVBERi0xLjcK... (base64-encoded PDF)"
}
POST https://www.weels.ca/api/shipment/change

Reschedule the delivery date for an active shipment. Only shipments with status 0 (Label Created), 1 (At Sorting Facility), or 2 (With Courier) can be rescheduled. Optionally sends an SMS notification to the recipient.

Request Fields

FieldTypeRequiredOptionsDescription
task_idintegerYesThe Weels shipment ID to reschedule.
delivery_datestringYesFormat: YYYY-MM-DDThe new delivery date.
notify_customerbooleanNotrue falseSend an SMS to the recipient about the date change. Default: false.

Example Request

curl -X POST https://www.weels.ca/api/shipment/change \
  -H "X-API-Key: your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{"task_id": 142, "delivery_date": "2026-03-01", "notify_customer": true}'

Success Response 200

{
  "success": true,
  "task_id": 142,
  "delivery_date": "2026-03-01",
  "old_date": "2026-02-15",
  "message": "Delivery date updated successfully.",
  "sms_sent": true
}

Error Responses

400 Not Modifiable

{
  "error": "This shipment can no longer be modified. Only pending or in-progress shipments can be rescheduled."
}

404 Not Found

{
  "error": "Shipment not found."
}
POST https://www.weels.ca/api/shipment/update-ref

Update the reference string on an existing shipment. The shipment must belong to your organization.

Request Fields

FieldTypeRequiredOptionsDescription
idintegerYesThe Weels shipment ID to update.
refstringYesMax 100 charsThe new reference string.

Example Request

curl -X POST https://www.weels.ca/api/shipment/update-ref \
  -H "X-API-Key: your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{"id": 142, "ref": "ORD-20260412-001"}'

Success Response 200

{
  "success": true,
  "task_id": 142,
  "reference": "ORD-20260412-001"
}

Error Responses

400 Missing Parameters

{
  "error": "Missing id parameter"
}

403 Forbidden

{
  "error": "Task does not belong to your organization"
}

404 Not Found

{
  "error": "Task not found"
}
Next: Schedule Pickup Related: Get Label
POST https://www.weels.ca/api/shipment/pickup-estimate

Get the estimated cost for a pickup from your organization’s registered address. No task is created — this is a read-only estimate. Pickups are free when your organization has 10 or more pending deliveries.

Request Fields

No fields are required. The API token identifies your organization, and the pickup address is your registered business address on file.

Example Request

curl -X POST https://www.weels.ca/api/shipment/pickup-estimate \
  -H "X-API-Key: your_api_token_here"

Success Response 200

{
  "covered": true,
  "base_rate": 16.00,
  "discount_pct": 15,
  "discount_amount": 2.40,
  "subtotal": 13.60,
  "tax_hst": 1.77,
  "total": 15.37,
  "free_pickup": false,
  "pending_deliveries": 4
}

Free Pickup Response

{
  "covered": true,
  "base_rate": 16.00,
  "discount_pct": 15,
  "discount_amount": 2.40,
  "subtotal": 0.00,
  "tax_hst": 0.00,
  "total": 0.00,
  "free_pickup": true,
  "pending_deliveries": 12
}

Response Fields

FieldTypeDescription
coveredbooleanWhether your organization’s address is within the pickup service area.
base_ratedecimalZone base rate for your postal code.
discount_pctdecimalYour organization’s discount percentage.
discount_amountdecimalDollar amount of discount applied.
subtotaldecimalAmount after discount (before tax). 0.00 if free pickup applies.
tax_hstdecimalHST (13%) applied to subtotal.
totaldecimalTotal charge including tax.
free_pickupbooleantrue if your organization has 10+ pending deliveries (pickup is free).
pending_deliveriesintegerNumber of pending deliveries your organization currently has.
Pickups are automatically free when your organization has 10 or more pending deliveries. Use this endpoint to check pricing and eligibility before scheduling.
POST https://www.weels.ca/api/shipment/pickup-create

Schedule a pickup from your organization’s registered address. A driver will be dispatched to collect your packages on the requested date. Your organization’s address on file is used as the pickup location.

Request Fields

FieldTypeRequiredOptionsDescription
pickup_datestringYesFormat: YYYY-MM-DDRequested pickup date. Cannot be in the past.
qtyintegerNoDefault: 1Estimated number of packages to be picked up.
referencestringNoMax 100 charsYour internal reference number.
order_numstringNoMax 100 charsYour order ID from your system.
notesstringNoMax 500 charsSpecial instructions for the driver (e.g. “Packages at the back door”, “Ask for reception”).
Custom Pickup Address — optional. If pickup_address1, pickup_city, and pickup_postalcode are all provided, the pickup will use this address instead of your organization’s registered address. The address must be within the Weels service area.
pickup_address1stringNoPickup street address (e.g. 200 Front St W)
pickup_address2stringNoUnit, suite, or loading dock number
pickup_citystringNoPickup city
pickup_provincestringNoDefault: org provincePickup province (2-letter code)
pickup_postalcodestringNoPickup postal code. Must be within Weels service area.
pickup_countrystringNoDefault: CAPickup country code (2-letter ISO)
pickup_companystringNoDefault: org nameCompany name at the pickup location
pickup_phonestringNoDefault: org phoneContact phone at the pickup location
pickup_emailstringNoDefault: org emailContact email at the pickup location
pickup_fnamestringNoDefault: org contactContact first name at the pickup location
pickup_lnamestringNoDefault: org contactContact last name at the pickup location

Example Request

curl -X POST https://www.weels.ca/api/shipment/pickup-create \
  -H "X-API-Key: your_api_token_here" \
  -d "pickup_date=2026-03-02" \
  -d "qty=5" \
  -d "notes=Packages at the loading dock"

Success Response 200

{
  "success": true,
  "task_id": 287,
  "pricing": {
    "base_rate": 16.00,
    "discount_pct": 15,
    "discount": 2.40,
    "subtotal": 13.60,
    "tax_hst": 1.77,
    "total": 15.37,
    "free_pickup": false
  },
  "message": "Pickup request created."
}

Free Pickup Response

{
  "success": true,
  "task_id": 288,
  "pricing": {
    "base_rate": 16.00,
    "discount_pct": 15,
    "discount": 2.40,
    "subtotal": 0.00,
    "tax_hst": 0.00,
    "total": 0.00,
    "free_pickup": true
  },
  "message": "Pickup request created (free — 12 pending deliveries)."
}

Error Responses

400 Validation Error

{
  "error": "Pickup date is required."
}

400 No Address

{
  "error": "Your organization does not have a complete address on file. Please contact support."
}

402 Insufficient Credit (prepay accounts, non-free pickup)

{
  "code": "insufficient_credit",
  "error": "Insufficient balance.",
  "balance": 5.00,
  "required": 15.37,
  "shortfall": 10.37
}
The pickup uses your organization’s registered address unless a custom address is provided. Pickups are free when you have 10+ pending deliveries — use the Pickup Estimate endpoint to check eligibility first. To cancel a pending pickup, use the Void Shipment endpoint with the returned task_id.
POST https://www.weels.ca/api/shipment/carrier-pickup-estimate

Get a price estimate for an on-demand carrier pickup (Canada Post or UPS) at your organization’s address. This does not schedule the pickup — use Schedule Carrier Pickup to confirm.

Request Fields

FieldTypeRequiredOptionsDescription
carrierstringYescanada_post, upsWhich carrier to request a pickup from
pickup_datestringNoYYYY-MM-DDRequested pickup date. Defaults to today.
ready_timestringNoHH:MM (24h)Earliest time packages are ready. Defaults to your organization’s pickup window start or 09:00.
close_timestringNoHH:MM (24h)Latest time the carrier can pick up. Defaults to your organization’s pickup window end or 17:00.

Example Request

curl -X POST https://www.weels.ca/api/shipment/carrier-pickup-estimate \
  -H "X-API-Key: your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{"carrier": "ups", "pickup_date": "2026-04-15"}'

Success Response 200

{
  "success": true,
  "carrier": "ups",
  "carrier_cost": 6.50,
  "markup_pct": 5,
  "markup_amount": 0.33,
  "subtotal": 6.83,
  "tax_hst": 0.89,
  "total": 7.72,
  "pending_packages": 3,
  "existing_pickup": null
}

Response Fields

FieldTypeDescription
carrierstringThe carrier quoted (canada_post or ups)
carrier_costnumberBase cost charged by the carrier
markup_pctnumberYour organization’s markup percentage
markup_amountnumberDollar amount of markup applied
subtotalnumberPre-tax total (carrier cost + markup)
tax_hstnumberHST charged
totalnumberTotal amount that will be charged
pending_packagesintegerNumber of pending carrier shipments awaiting pickup
existing_pickupobject|nullIf a pickup is already scheduled for this date, returns task_id and confirmation
The carrier must be enabled for your organization. Canada Post pickups have a flat rate; UPS rates are dynamic based on location and package count. Use this endpoint to preview pricing before scheduling.
POST https://www.weels.ca/api/shipment/carrier-pickup-create

Schedule an on-demand carrier pickup (Canada Post or UPS) at your organization’s registered address. The carrier will dispatch a driver to collect your packages. Your prepay balance is charged immediately.

Request Fields

FieldTypeRequiredOptionsDescription
carrierstringYescanada_post, upsWhich carrier to request a pickup from
pickup_datestringYesYYYY-MM-DDRequested pickup date (today or future business day)
ready_timestringNoHH:MM (24h)Earliest time packages are ready. Defaults to your organization’s pickup window or 09:00.
close_timestringNoHH:MM (24h)Latest time the carrier can pick up. Defaults to your organization’s pickup window or 17:00.
pickup_locationstringNoSpecific location at the address (e.g. “Loading dock”, “Front desk”)
instructionsstringNoSpecial instructions for the carrier driver
notesstringNoInternal notes (stored on the pickup task)

Example Request

curl -X POST https://www.weels.ca/api/shipment/carrier-pickup-create \
  -H "X-API-Key: your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{"carrier": "ups", "pickup_date": "2026-04-15", "pickup_location": "Loading dock", "instructions": "Ring buzzer #3"}'

Success Response 200

{
  "success": true,
  "task_id": 30004012,
  "carrier_pickup_id": "WLP3E843243",
  "pricing": {
    "carrier_cost": 6.50,
    "markup_pct": 5,
    "markup_amount": 0.33,
    "subtotal": 6.83,
    "tax_hst": 0.89,
    "total": 7.72
  },
  "message": "UPS pickup scheduled for 2026-04-15."
}

Error Responses

400 Validation Error

{
  "success": false,
  "error": "Valid carrier is required (canada_post or ups)."
}

422 No Pending Packages

{
  "success": false,
  "error": "No pending carrier deliveries found for pickup."
}

402 Insufficient Credit

{
  "success": false,
  "code": "insufficient_credit",
  "error": "Insufficient balance.",
  "balance": 2.50,
  "required": 7.72,
  "shortfall": 5.22
}
The carrier must be enabled for your organization with pickup mode set to “carrier”. Use Carrier Pickup Estimate to preview pricing. To cancel a scheduled carrier pickup, use the Cancel Carrier Pickup endpoint.
POST https://www.weels.ca/api/shipment/carrier-pickup-cancel

Cancel a previously scheduled carrier pickup. The carrier’s API is called to cancel the pickup, the task status is updated, and prepay accounts receive a refund to their credit balance.

Request Fields

FieldTypeRequiredDescription
task_idintegerYesThe pickup task ID returned from Schedule Carrier Pickup

Example Request

curl -X POST https://www.weels.ca/api/shipment/carrier-pickup-cancel \
  -H "X-API-Key: your_api_token_here" \
  -H "Content-Type: application/json" \
  -d '{"task_id": 30004012}'

Success Response 200

{
  "success": true,
  "message": "Carrier pickup cancelled.",
  "refund": 7.72
}

Response Fields

FieldTypeDescription
messagestringConfirmation message
refundnumberAmount refunded to your credit balance (prepay accounts only)

Error Responses

400 Missing Fields

{
  "success": false,
  "error": "vendor_id and task_id are required."
}

404 Not Found

{
  "success": false,
  "error": "Pickup task not found or does not belong to this vendor."
}
Cancellation must be made before the carrier dispatches. Some carriers may reject cancellation requests for same-day pickups that are already in progress.
GET https://www.weels.ca/api/drivers/{mm/dd/yyyy}

List all delivery buckets (route groups) and their packages for a specific delivery date. Each bucket shows the assigned driver, if any. Only includes shipments with status 0, 1, or 2 (active, pre-delivery). If no date is provided, defaults to today.

URL Parameters

ParameterTypeRequiredDescription
{mm/dd/yyyy}stringNoDelivery date in mm/dd/yyyy format. Defaults to today if omitted.

Example Request

curl -H "X-API-Key: your_api_token_here" \
  "https://www.weels.ca/api/drivers/02/18/2026"

Success Response 200

{
  "buckets": [
    {
      "bucket_id": 12,
      "label": "Markham North",
      "status": "claimed",
      "driver": "John Smith",
      "packages": [
        {
          "reference": "REF-001",
          "shipment_id": 142,
          "status": 1,
          "confirmed": 0,
          "order_num": "ORD-12345",
          "name": "Jane Doe"
        }
      ]
    },
    {
      "bucket_id": 13,
      "label": "Scarborough East",
      "status": "published",
      "driver": null,
      "packages": [
        {
          "reference": "REF-002",
          "shipment_id": 143,
          "status": 0,
          "confirmed": 0,
          "order_num": "ORD-12346",
          "name": "Bob Wilson"
        }
      ]
    },
    {
      "bucket_id": null,
      "label": "Unbucketed",
      "status": null,
      "driver": null,
      "packages": [
        {
          "reference": "REF-003",
          "shipment_id": 144,
          "status": 0,
          "confirmed": 0,
          "order_num": "ORD-12347",
          "name": "Alice Chen"
        }
      ]
    }
  ]
}

Response Fields

FieldTypeDescription
bucket_idinteger|nullThe delivery bucket ID, or null for shipments not yet assigned to a bucket.
labelstringThe bucket name (e.g. "Markham North"). "Unbucketed" for unassigned shipments.
statusstring|nullBucket lifecycle status: draft, published, claimed, in_progress, completed, or cancelled. null for unbucketed shipments.
driverstring|nullFull name of the driver assigned to this bucket, or null if unclaimed.
packages[].referencestringYour reference/tracking number.
packages[].shipment_idintegerWeels shipment ID.
packages[].statusintegerNumeric shipment status (see Shipment Statuses).
packages[].confirmedinteger1 if the delivery has been completed, 0 otherwise.
packages[].order_numstringYour order number.
packages[].namestringRecipient full name.
Shipments are grouped by delivery bucket (optimized route group). Packages within each bucket are ordered by their route stop order. Shipments not yet assigned to any bucket appear under an "Unbucketed" entry at the end. The driver field shows who has claimed the bucket — it is null until a driver claims it.
GET https://www.weels.ca/api/cities

Returns a list of distinct city names within the Weels delivery network. This endpoint is public and does not require authentication.

Example Request

curl "https://www.weels.ca/api/cities"

Success Response 200

[
  { "name": "Ajax" },
  { "name": "Aurora" },
  { "name": "Brampton" },
  { "name": "Etobicoke" },
  { "name": "Maple" },
  { "name": "Markham" },
  { "name": "Mississauga" },
  { "name": "Toronto" }
]
The response is a flat JSON array of city objects. Cities are sorted alphabetically and returned with title-cased names. Results are cached for one hour.
GET https://www.weels.ca/api/days

Returns the available delivery days for a postal code. Uses longest-prefix matching to find the most specific zone. This endpoint is public and does not require authentication.

Request Fields

FieldTypeRequiredOptionsDescription
postalstringYesCanadian postal code (at least the 3-character FSA). Spaces and special characters are stripped automatically.

Example Request

curl "https://www.weels.ca/api/days?postal=L6A1G2"

Success Response (Covered) 200

{
  "success": true,
  "covered": true,
  "days": ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday"],
  "dday": "Saturday,Sunday,Monday,Tuesday,Wednesday"
}

Response (Not Covered) 200

{
  "success": false,
  "covered": false
}

Error Responses

400 Invalid Postal Code

{
  "error": "Postal code must be at least 3 characters."
}
The days array contains full day names. The dday field provides the same data as a comma-separated string for convenience. If a zone is found but has no delivery days configured, defaults to Saturday through Wednesday.
GET https://www.weels.ca/api/blackouts

Returns all active, future blackout dates — days when deliveries are not available, labels cannot be created, and rate ETAs are adjusted. Each entry includes a preferred date (the next available delivery day after the blackout) for use in scheduling.

Example Request

curl -H "X-API-Key: your_api_token_here" \
  "https://www.weels.ca/api/blackouts"

Success Response 200

{
  "success": true,
  "blackouts": [
    {
      "date": "2026-04-03",
      "description": "Good Friday",
      "recurring": 0,
      "preferred": "2026-04-04"
    },
    {
      "date": "2026-07-01",
      "description": "Canada Day",
      "recurring": 1,
      "preferred": "2026-07-02"
    },
    {
      "date": "2026-12-25",
      "description": "Christmas Day",
      "recurring": 1,
      "preferred": "2026-12-27"
    }
  ]
}

Response Fields

FieldTypeDescription
datestringThe blackout date (YYYY-MM-DD). For recurring dates, projected to the current or next year.
descriptionstring|nullHuman-readable label (e.g. "Christmas Day").
recurringinteger1 if the blackout repeats every year, 0 if one-time.
preferredstringThe next available delivery day after the blackout (YYYY-MM-DD). Skips consecutive blackout dates.
Use the preferred date to adjust ETAs when a delivery would land on a blackout date. If multiple blackout dates are consecutive (e.g. Dec 25 & 26), preferred will be the first available day after all of them. Only active, future blackout dates are returned. Recurring dates are automatically projected to the current or next year.
POST https://www.weels.ca/api/shipment/manifest

Manage Canada Post manifests. Manifests must be transmitted to Canada Post before your packages can be picked up. This endpoint lets you check pending shipments, transmit manifests, and retrieve manifest history and PDFs.

This endpoint requires carrier manifest management to be enabled for your organization. If you receive an error, contact support to have it enabled.

Request Fields

FieldTypeRequiredOptionsDescription
actionstringYespending, transmit, list, detail, downloadThe manifest operation to perform.
manifest_idintegerConditionalRequired for detail. Optional for download (defaults to latest manifest).
pageintegerNoDefault: 1Page number for paginated results (used with list).
limitintegerNoDefault: 25, Max: 100Results per page (used with list).

Actions

ActionDescription
pendingList all unmanifested Canada Post shipments awaiting transmission.
transmitTransmit all pending shipments to Canada Post as a manifest. Returns the manifest PDF.
listPaginated history of all transmitted manifests.
detailList shipments within a specific manifest (packing list). Requires manifest_id.
downloadGet signed download URLs for manifest PDFs. Optional manifest_id (defaults to latest).

Pending Shipments

Returns all carrier shipments that have not yet been transmitted to Canada Post.

curl -X POST https://www.weels.ca/api/shipment/manifest \
  -H "X-API-Key: your_api_token_here" \
  -d "action=pending"

Pending Response 200

{
  "success": true,
  "unmanifested_count": 3,
  "shipments": [
    {
      "task_id": 1234,
      "reference": "ORD-001",
      "order_num": "WC-5890",
      "tracking_number": "1234567890123456",
      "service_code": "DOM.EP",
      "destination": "Toronto, ON",
      "created_at": "2026-04-13 10:30:00"
    },
    {
      "task_id": 1235,
      "reference": "ORD-002",
      "order_num": "",
      "tracking_number": "1234567890123457",
      "service_code": "DOM.XP",
      "destination": "Ottawa, ON",
      "created_at": "2026-04-13 11:15:00"
    }
  ]
}

Transmit Manifest

Transmits all pending shipments to Canada Post as a manifest. Once transmitted, the shipments are finalized and a manifest PDF is generated. This is equivalent to “closing out” the day’s shipments.

curl -X POST https://www.weels.ca/api/shipment/manifest \
  -H "X-API-Key: your_api_token_here" \
  -d "action=transmit"

Transmit Response 200

{
  "success": true,
  "manifest_id": 42,
  "task_count": 5,
  "documents": [
    {
      "label": "Manifest",
      "s3_key": "manifests/canada_post/vendor-8/...",
      "url": "https://..."
    }
  ],
  "manifest_pdf": "https://..."
}

List Manifests

Returns a paginated list of all transmitted manifests for your organization, with signed download URLs for the manifest PDFs.

curl -X POST https://www.weels.ca/api/shipment/manifest \
  -H "X-API-Key: your_api_token_here" \
  -d "action=list" \
  -d "page=1" \
  -d "limit=10"

List Response 200

{
  "success": true,
  "manifests": [
    {
      "id": 42,
      "manifest_id": "12345678",
      "task_count": 5,
      "status": "transmitted",
      "transmitted_at": "2026-04-13 16:00:00",
      "download_url": "https://..."
    },
    {
      "id": 41,
      "manifest_id": "12345677",
      "task_count": 3,
      "status": "transmitted",
      "transmitted_at": "2026-04-12 16:00:00",
      "download_url": "https://..."
    }
  ],
  "total": 15,
  "page": 1,
  "pages": 2
}

Manifest Detail

Returns the list of shipments included in a specific manifest (packing list). Useful for verifying which packages were included in a transmission.

curl -X POST https://www.weels.ca/api/shipment/manifest \
  -H "X-API-Key: your_api_token_here" \
  -d "action=detail" \
  -d "manifest_id=42"

Detail Response 200

{
  "success": true,
  "manifest_id": 42,
  "carrier": "canada_post",
  "task_count": 5,
  "status": "transmitted",
  "transmitted_at": "2026-04-13 16:00:00",
  "shipments": [
    {
      "id": 1234,
      "reference": "ORD-001",
      "order_num": "WC-5890",
      "carrier_tracking_number": "1234567890123456",
      "carrier_service_code": "DOM.EP",
      "destination_fname": "John",
      "destination_lname": "Doe",
      "destination_company": "",
      "destination_address1": "200 Front St W",
      "destination_city": "Toronto",
      "destination_province": "ON",
      "destination_postalcode": "M5V3K2",
      "qty": 1,
      "weight": "0.500"
    }
  ]
}

Download Manifest PDF

Returns signed download URLs for the manifest PDF files. If manifest_id is omitted, returns the most recent manifest. URLs expire after 1 hour.

curl -X POST https://www.weels.ca/api/shipment/manifest \
  -H "X-API-Key: your_api_token_here" \
  -d "action=download" \
  -d "manifest_id=42"

Download Response 200

{
  "success": true,
  "documents": [
    {
      "label": "Manifest",
      "url": "https://...",
      "vendor": true
    }
  ]
}

Error Responses

400 Not Enabled

{
  "error": "Carrier manifest management is not enabled for this organization."
}

422 No Pending Shipments

{
  "success": false,
  "error": "No unmanifested Canada Post shipments for this vendor."
}

404 Manifest Not Found

{
  "error": "Manifest not found."
}
Manifests are required by Canada Post before packages can be scanned and picked up. A typical workflow is: create shipments throughout the day, then call transmit once at end-of-day to close out the manifest. The transmit call may take a few seconds as it communicates with Canada Post’s servers and generates the PDF artifact. Download URLs expire after 1 hour — use the download action to generate fresh URLs.

Error Handling

All errors return a JSON object with an error field. Some errors also include a code field for programmatic handling.

HTTP CodeMeaning
400Bad Request -- validation errors, invalid input, address verification failed
401Unauthorized -- invalid or missing API token
402Payment Required -- insufficient credit balance (prepay accounts)
403Forbidden -- token valid but not authorized for this resource
404Not Found -- shipment does not exist
405Method Not Allowed -- wrong HTTP method
429Too Many Requests -- rate limit exceeded
502Bad Gateway -- delivery service error

Shipment Statuses

Each shipment has a numeric status code. These are the possible values:

CodeLabel
0Delivery label has been created
1Package received at sorting facility
2Package is with courier
3Out for delivery
4Delivered
5Delivery label has been cancelled
6Failed to deliver
7Package picked up from vendor
8New shipment created
9Package en route to transfer hub
10Package held after failed delivery
11Package reported lost — insurance claim filed
12Refund requested

Webhooks

Webhooks let Weels push real-time event notifications to your server when task statuses change — no polling required. You register an HTTPS endpoint and choose which events to subscribe to. Weels sends a signed POST request to your URL whenever a matching event occurs.

Webhooks are managed from the Developer tab of your Organization settings. Your endpoint must be publicly reachable and return a 2xx response — this is verified at registration time.

Events

EventFired when
task.shippedA shipment is created and assigned to a delivery date
task.startedA driver starts a delivery or pickup
task.arrivedThe driver marks arrival at the destination
task.completedThe delivery or pickup is successfully completed
task.failedThe delivery or pickup attempt was unsuccessful

Payload

Every webhook request is an HTTP POST with a Content-Type: application/json body in the following shape:

{
  "event": "task.completed",
  "timestamp": "2026-02-20T14:32:00-05:00",
  "data": {
    "task_id": 48291,
    "order_num": "WLS-10042",
    "task_type": "delivery",
    "status": 4,
    "driver": "Sam B.",
    "tracking": "https://www.weels.ca/track/48291",
    "destination": {
      "name": "Jane Smith",
      "address": "123 King St W, Toronto, ON M5H 1J9"
    },
    "started_at": "2026-02-20T13:58:00-05:00",
    "completed_at": "2026-02-20T14:32:00-05:00"
  }
}

Payload Fields

FieldTypeDescription
eventstringThe event name (see Events above)
timestampstringISO 8601 datetime when the event was fired
data.task_idintegerInternal Weels task ID
data.order_numstring|nullYour order/reference number as submitted
data.task_typestringdelivery or pickup
data.statusintegerNumeric task status code (see Shipment Statuses)
data.driverstring|nullDriver's first name and last initial (e.g. Sam B.)
data.trackingstringPublic tracking page URL
data.destination.namestringRecipient full name
data.destination.addressstringFull destination address
data.started_atstring|nullISO 8601 datetime when the driver started the task
data.completed_atstring|nullISO 8601 datetime when the task was completed

Verifying Signatures

Every request includes an X-Weels-Signature header. Verify it before processing the payload to confirm the request came from Weels and hasn't been tampered with.

Where is my signing secret? When you register a webhook under Organization Settings → Developer → Webhooks, the signing secret is displayed once immediately after creation. Copy it and store it securely in your environment variables — it cannot be retrieved again. If you lose it, delete the webhook and create a new one.

The signature is an HMAC-SHA256 of <timestamp>.<raw_body>, keyed with your secret. Both the signature and timestamp are sent as headers:

X-Weels-Signature:  sha256=<HMAC-SHA256(timestamp + "." + raw_body, secret)>
X-Weels-Timestamp:  1740000000

To verify, reconstruct the signed content from the timestamp header and the raw body, then compare signatures. Reject payloads older than 5 minutes to prevent replay attacks:

$payload   = file_get_contents('php://input');
$secret    = 'your_webhook_secret';
$timestamp = $_SERVER['HTTP_X_WEELS_TIMESTAMP'] ?? '';
$received  = $_SERVER['HTTP_X_WEELS_SIGNATURE'] ?? '';

// Reject payloads older than 5 minutes
if (abs(time() - (int)$timestamp) > 300) {
    http_response_code(401);
    exit('Timestamp too old');
}

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $payload, $secret);

if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit('Invalid signature');
}

$event = json_decode($payload, true);
// process $event...

Request Headers

HeaderValue
Content-Typeapplication/json
X-Weels-EventThe event name (e.g. task.completed)
X-Weels-SignatureHMAC-SHA256 signature of timestamp.body, prefixed with sha256=
X-Weels-TimestampUnix timestamp (seconds) when the request was signed — use for replay protection
User-AgentWeels-Webhook/1.0

Reliability & Auto-Disable

  • Weels expects your endpoint to respond with a 2xx status within 10 seconds. Any other response (including timeouts) is considered a failure.
  • After 5 consecutive delivery failures, the webhook is automatically disabled to prevent unnecessary traffic to a broken endpoint.
  • You'll see the webhook marked as Failed in your Developer settings. Fix your endpoint, then re-enable it — the failure counter resets automatically on the next successful delivery.
  • Use the Test button in your Developer settings to send a webhook.test ping and confirm your endpoint is reachable at any time.
Your endpoint must respond quickly. Do not perform heavy processing synchronously — acknowledge the webhook with a 200 immediately and handle processing in a background job.

Service Area Widget

Embed a delivery zone checker on any website with a single script tag. The widget lets your customers verify whether their postal code is within our service area — no API key required.

Installation

Add this snippet anywhere on your page:

<div id="weels-service-area"></div>
<script src="https://www.weels.ca/js/embed-service-area.js"></script>

The widget is self-contained — zero dependencies, no jQuery, no external stylesheets. It injects its own scoped styles and calls the public /api/service-area endpoint directly.

Customization

Add data- attributes to the script tag to customize:

AttributeDefaultDescription
data-themelightlight or dark
data-accent#10b981Accent color for button and focus ring
data-button-textCheck availabilityButton label
data-placeholderEnter your postal codeInput placeholder
data-target#weels-service-areaCSS selector for container element

Example

<div id="weels-service-area"></div>
<script src="https://www.weels.ca/js/embed-service-area.js"
  data-theme="dark"
  data-accent="#6366f1"
  data-button-text="Check my area">
</script>
See the live demo and full documentation for a preview of both light and dark themes.