For businesses

Shipping API

Book Amasyl Express shipments from your own website, store or warehouse system, and get a print-ready 4×6 label back. Same routes, same business rate and same drivers as booking on our site — without anyone retyping an address.

Getting started

  1. Have an approved business account. If you don't, apply from your dashboard — we'll approve it and set your rate.
  2. In Dashboard → API access, save a card with Stripe. Every shipment you book over the API is charged to it at the moment it's booked, at your business rate. The card is held by Stripe; we never see the number.
  3. Create an API key on the same page. It's shown once — copy it straight into your system's secrets.

Send the key on every request as a bearer token. Requests and responses are JSON. All amounts are CAD.

Authorization: Bearer amx_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Base URL: https://amasylexpress.ca/api/v1

Get a price — POST /quote

What a shipment would cost, without booking it. Use it to show shipping at your own checkout. It runs the same checks and pricing as creating a shipment, so the quote is what you'll be charged.recipient_name anditem_descriptionaren't needed for a quote.

curl https://amasylexpress.ca/api/v1/quote \
  -H "Authorization: Bearer $AMASYL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_address": "123 9 Ave SE, Calgary, AB T2G 0S7",
    "to_address": "200 Portage Ave, Winnipeg, MB R3C 3X1",
    "package_type": "box",
    "weight_kg": 8,
    "quantity": 2,
    "pickup_date": "2026-10-01"
  }'
200 OK
{
  "quote": {
    "route": "Calgary ↔ Winnipeg",
    "pickup_date": "2026-09-30",
    "pickup_window": "day-before-afternoon-evening",
    "pickup_time": "Day before, 1:00 PM - 10:00 PM",
    "currency": "CAD",
    "subtotal": 105.00,
    "business_discount": 10.00,
    "volume_discount": 0,
    "insurance": 0,
    "tax": 5.25,
    "total": 110.25
  }
}

Figures above are illustrative. Note pickup_date in the answer can be the day before the one you asked for: long-haul trucks leave overnight, so their collection happens the evening before.

Book a shipment — POST /shipments

Books the shipment and charges your card. Returns the tracking number and label link.

FieldWhat it is
from_addressrequiredFull pickup address: street, city, province. "3616 14 Ave North, Lethbridge, AB T1H 6E7".
to_addressrequiredFull delivery address, same format.
package_typerequiredenvelope, box, pallet or cold_pack (frozen, on our scheduled refrigerated run).
weight_kgrequiredWeight of ONE box, in kilograms. Pallets start at 500 kg; frozen boxes top out at 25 kg.
quantityoptionalNumber of boxes, 1–500. Defaults to 1. You get one label page per box.
pickup_daterequiredYYYY-MM-DD. Must be a day we run the route — the error tells you which days do.
pickup_windowoptionalCollection window. Leave it out to take the earliest one still open; send one we don't run and the error lists the valid values.
recipient_namerequired*Who it's going to. Printed large on the label.
recipient_phoneoptionalStrongly recommended — the driver calls it if nobody answers the door.
recipient_emailoptional
item_descriptionrequired*What's in the box, in plain words. Printed on the label.
sender_nameoptionalDefaults to your company name.
sender_phoneoptional
priorityoptionalstandard (default) or priority.
fragileoptionaltrue to mark the shipment fragile.
signature_requiredoptionaltrue to require a signature on delivery.
declared_value_cadoptionalDeclared value for insurance.
dimensionsoptionalFree text, e.g. "40x30x30 cm".
notesoptionalFor the driver: buzzer code, side door, who to ask for.
external_refoptionalYOUR order number. Makes retries safe — see below. Strongly recommended.

* Required to book; not needed for a quote.

curl https://amasylexpress.ca/api/v1/shipments \
  -H "Authorization: Bearer $AMASYL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_ref": "ORDER-10432",
    "from_address": "123 9 Ave SE, Calgary, AB T2G 0S7",
    "to_address": "200 Portage Ave, Winnipeg, MB R3C 3X1",
    "package_type": "box",
    "weight_kg": 8,
    "quantity": 2,
    "pickup_date": "2026-10-01",
    "recipient_name": "Ada Obi",
    "recipient_phone": "204-555-0123",
    "item_description": "Auto parts"
  }'
201 Created
{
  "shipment": {
    "tracking_number": "AMSYWG093001",
    "external_ref": "ORDER-10432",
    "status": "pending",
    "from_address": "123 9 Ave SE, Calgary, AB T2G 0S7",
    "to_address": "200 Portage Ave, Winnipeg, MB R3C 3X1",
    "package_type": "box",
    "quantity": 2,
    "weight_kg": 8,
    "pickup_date": "2026-09-30",
    "pickup_time": "Day before, 1:00 PM - 10:00 PM",
    "delivered_at": null,
    "currency": "CAD",
    "subtotal": 105.00,
    "tax": 5.25,
    "total": 110.25,
    "paid": true,
    "tracking_url": "https://amasylexpress.ca/track?tracking=AMSYWG093001",
    "label_pdf_url": "https://amasylexpress.ca/api/v1/shipments/AMSYWG093001/label"
  },
  "quote": { ... }
}

You get a confirmation email with the invoice attached, the same as a website booking. If the card is declined, nothing is booked and you get a 402 — see errors.

Print the label — GET /shipments/{tracking_number}/label

A 4×6 PDF, one page per box, each marked “Box 2 of 3”. Send it straight to a thermal label printer or print it on plain paper and tape it on. Every box needs its own label — a box without one can't be collected.

curl https://amasylexpress.ca/api/v1/shipments/AMSYWG093001/label \
  -H "Authorization: Bearer $AMASYL_API_KEY" \
  -o label.pdf

Track — GET /shipments/{tracking_number}

Returns the same shipment object as above with its current status: pending, confirmed, in_transit, delivered or cancelled. You can only see shipments booked on your own account.

curl https://amasylexpress.ca/api/v1/shipments/AMSYWG093001 \
  -H "Authorization: Bearer $AMASYL_API_KEY"

# Or by your own order number:
curl "https://amasylexpress.ca/api/v1/shipments/lookup?external_ref=ORDER-10432" \
  -H "Authorization: Bearer $AMASYL_API_KEY"

Retries and double charges

Networks drop connections. If your request to create a shipment times out, you don't know whether it was booked — and retrying blindly could book it twice.

Always send external_ref with your own order number. Then a retry with the same key and the same external_ref returns the original shipment with "replayed": true — never a second shipment and never a second charge. If your first attempt was declined, the same external_ref can be used again once the card is sorted out.

Errors

Every error has the same shape. Branch on code; show or log message.

{ "error": { "code": "card_declined", "message": "Your card was declined. Nothing was booked." } }
HTTPcodeWhat to do
401missing_api_key / invalid_api_key / revoked_api_keyCheck the Authorization header, or create a new key.
402no_card_on_fileSave a card with Stripe in Dashboard → API access.
402card_declinedThe card was declined. Nothing was booked.
402card_authentication_requiredThe bank wants the cardholder to approve this one. Book it on the website, or save a different card.
403not_an_approved_businessAPI access is for approved business accounts.
404not_foundNo shipment with that reference on your account.
409already_in_progressSame external_ref, still being created. Look it up in a few seconds.
422outside_service_areaAn address we don't normally cover. Contact us and we'll arrange it.
400not_an_operating_day, invalid_pickup_window, pickup_window_closed, …The message says exactly what to change.
429rate_limitedOver 120 requests a minute on one key. Wait retry_after_seconds.
503bookings_paused / stripe_unreachableTemporary. Nothing was booked or charged — retry.

Limit: 120 requests a minute per key.

Questions?

Call us on (403) 635-0666 or get in touch. We're happy to talk to your developer directly.