# MesColis & ShipSmarter: Full Context for AI Models & Agents > This is the extended (`llms-full.txt`) companion to `/llms.txt`. It concatenates the > full company context AND the complete REST API reference into one document so an LLM > or agent can ingest everything in a single fetch, without scraping HTML or the > OpenAPI spec. Source of truth: `/llms.txt` + `/openapi.yaml` (served at > `https://www.mescolis.ca/openapi.yaml`). Human API docs: https://www.mescolis.ca/api-docs --- ## 1. What MesColis / ShipSmarter is MesColis is a Canada-rooted **B2B shipping SaaS** that lets small and medium businesses compare real-time shipping rates across major carriers and buy labels for eligible services. **ShipSmarter.ca** is the English-Canadian sister brand: the **same company, same platform, same codebase, identical feature set**; only the default language (FR vs EN) and branding differ. - Founded **2024**, based in **Laval (Greater Montréal), Quebec, Canada**. - Bilingual (French / English). Free signup, no contract, no minimum volume. - Available shipping origins and destinations depend on the quote and active carrier accounts; a country landing page does not establish service availability. - **Official Canada Post Technology Partner** (Developer Program): direct Canada Post API integration. **What MesColis is NOT:** - Not a carrier: it operates no trucks, planes, or warehouses; it is software that interfaces with established carriers. - Not an LTL/FTL freight broker: it focuses on parcel shipping (≈1 g to ~30 kg per piece). - Not free past the Starter tier: Pro/Enterprise have a monthly subscription; all plans pay carrier rates at MesColis-negotiated prices. **Aggregator, not consolidator:** MesColis/ShipSmarter are **aggregators**: the carrier picks up directly from you and you choose among multiple carriers. Consolidators (Chit Chats, Stallion Express) physically take possession of parcels and batch them. Alternatives in the market: ShipTime, Secureship, FlagShip, netParcel. --- ## 2. Core capabilities Availability depends on plan, account connection and service eligibility. Check the current quote and terms before purchase. A listed integration does not establish that it is connected for every customer. - **Compare rates instantly** across the carriers available for your parcel from one screen. - **Real-time rates at checkout (Shopify)**: show the exact discounted price to customers while they buy (subject to Shopify carrier-calculated shipping eligibility). WooCommerce supports order import and fulfillment synchronization; live checkout rates are not claimed here. - **Buy and print labels** for eligible parcels, subject to carrier and destination rules. - **Schedule pickups** directly with the carrier, with no separate phone call. - **Branded customer tracking**: tracking pages customised to your brand (no redirect to the carrier), no code required. - **Smart parcel lockers**: parcel drop-off and pickup; confirm active locations, opening hours and eligible carriers. - **Customer returns portal** + prepaid return labels (no coding required). - **BYOA (Bring Your Own Account)**: connect your own Canada Post / UPS / FedEx account to ship on your negotiated rates (Pro/Enterprise). - **Customs handling** for US and international shipments: HS-code lookup, customs forms, AI-assisted customs scan, duty estimates. - **No-code integrations**: Shopify, WooCommerce, BigCommerce, plus a public REST API (see §7). - **Pricing**: pooled-volume carrier rates, with a transparent markup shown before you confirm each shipment. Recipient notification rule: MesColis handles ALL customer-facing tracking communication; carrier emails are never sent to your customer. --- ## 3. Who it is for Small and medium businesses shipping **10–10,000 parcels per month**: e-commerce merchants on Shopify / WooCommerce / BigCommerce or custom stacks, plus wholesalers, distributors, and retailers who need multi-carrier comparison. --- ## 4. Pricing Website subscriptions are billed in CAD. Postage, displayed service fees and applicable taxes are additional. The Shopify app listing quotes subscriptions in USD; its approval screen controls the amount and currency for that billing channel. | Plan | Price | Includes | |------|-------|----------| | **Starter** | $0/month, pay per shipment | Up to 50 shipments/month, 1 user, basic carrier access. No credit card required. | | **Pro** | $20/month | 500 shipments/month, 5 users, carriers available for the quote, Shopify auto-fulfill, advanced analytics, priority support, REST API access. | | **Enterprise** | $99/month | Unlimited shipments/month, unlimited users, dedicated account manager, custom carrier rates, REST API, white-label tracking; confirm SSO eligibility and agreed service terms. | All plans include: real-time rate shopping, label generation, pickup scheduling, tracking, returns, bilingual FR/EN, customer support. API access (public REST API, webhooks, API-key + scope auth) is available on **Pro and Enterprise**. --- ## 5. Carrier options This list describes integration options, not activation of every carrier. Availability depends on origin, destination, parcel, account and current quote. Canada Post, FedEx (Ground / Express / 2-Day / Overnight), UPS (Ground / Express / Worldwide), DHL Express, Canpar, Nationex, GLS Canada, UniUni, Uber Direct (same-day in major Canadian cities), Intelcom, Freightcom, GlobalPost. --- ## 6. Key URLs | Page | MesColis | ShipSmarter | |------|------------------|---------------------| | Home | https://www.mescolis.ca | https://www.shipsmarter.ca | | About | https://www.mescolis.ca/about | — | | Help Centre | https://www.mescolis.ca/help | — | | Blog | https://www.mescolis.ca/blog | https://www.shipsmarter.ca/blog | | Shipping Guide | https://www.mescolis.ca/guide | — | | Operating Principles | https://www.mescolis.ca/principles | — | | Integrations | https://www.mescolis.ca/integrations | — | | FAQ (AEO) | https://www.mescolis.ca/faq | — | | Find Us (location) | https://www.mescolis.ca/location | — | | Comparisons | — | /vs/shiptime · /vs/secureship · /vs/chit-chats · /vs/netparcel | | API docs | https://www.mescolis.ca/api-docs | — | | Privacy | https://www.mescolis.ca/privacy | — | | Terms | https://www.mescolis.ca/terms | — | | Trust & Security | https://www.mescolis.ca/trust | — | Country landing pages: `/us`, `/gb`, `/fr`, `/de`, `/au`, `/nz`, `/be`, `/nl`, `/hk`, `/sg`. --- ## 7. REST API reference > Full machine-readable spec: **`https://www.mescolis.ca/openapi.yaml`** (OpenAPI 3.1). > Human docs: **https://www.mescolis.ca/api-docs**. API access requires a **Pro** or **Enterprise** plan. **Base URL:** `https://www.mescolis.ca/api/v1` ### Authentication Every request must include a Bearer API key: ``` Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` Generate keys in **Settings → Developer Hub** (shown only once; prefix `mc_live_`). Missing/invalid key → **401**; key lacking the required scope → **403**. ### Scopes (least privilege) | Scope | Allows | |-------|--------| | `rates:read` | Compare shipping rates | | `shipments:read` | Read shipment records | | `shipments:write` | **Buy labels.** Spends real money. Never granted by default, must be requested by name, and cannot be granted through an OAuth consent screen | | `track:read` | Track packages by tracking number | | `addresses:read` | Read address book entries | | `addresses:write` | Create, update, delete addresses | ### Endpoints | Method & path | Scope | Description | Rate limit | |---------------|-------|-------------|------------| | `GET /rates` | `rates:read` | Compare real-time rates across all active carriers (markup already included). Query: `from_postal`, `to_postal`, `weight_kg`, `length_cm`, `width_cm`, `height_cm`, plus optional `residential`, `insured_value` (0-5000) and `signature_required`. Each rate carries a `rate_id`. | 30/min | | `POST /rates` | `rates:read` | Same quote, JSON body, **up to 10 parcels in one shipment**. Body: `from`/`to` (`postal`, `country`), `packages[]` (metric), plus the same optional `insured_value`, `signature_required`, `declared_value`, `hs_code`, `category`, `description`. The `rate_id` it returns is signed against those exact parcels. | 30/min | | `POST /shipments` | `shipments:write` | **Buy a label.** Body: `rate_id` (from `/rates`, valid 2 h), `from`, `to`, `packages` (metric). The caller never sends a price: `rate_id` is a signed token carrying the quoted price. Send `Idempotency-Key` so a retry cannot double-buy. Settles from the prepaid wallet. | 5/min | | `POST /shipments/{id}/cancel` | `shipments:write` | **Void a label and refund it.** Cancels at the carrier, refunds the wallet or card, un-fulfils a linked storefront order. Refuses with 409 once the parcel is collected or the payment captured; already-cancelled returns 200 with `already_cancelled: true`, so retries are safe. | 20/min | | `GET /shipments` | `shipments:read` | List/filter your shipment records (paginated). | 60/min | | `GET /shipments/{id}` | `shipments:read` | Get one shipment by id. | 60/min | | `POST /batch` | `shipments:write` | **Buy up to 25 labels in one call.** Body: `shipments[]`, each entry exactly a `POST /shipments` body. Same purchase path per item, so wallet settlement, plan limits and `shipment.created` all apply. Partial success is normal: it returns 200 with per-item `results` and a `failed` count. **Send `Idempotency-Key`**: each item derives `-`, so a retry after a timeout replays instead of re-buying. Pro and above. | 4/min | | `GET /batch` | `shipments:read` | List batch jobs, including CSV batches run from the dashboard. | 30/min | | `POST /returns` | `shipments:write` | **Open a return** on one of your own shipments. Body: `shipment_id`, optional `reason`/`notes`. Fires `return.created` and mints the return label where the carrier can mirror the original. A shipment that already has an open return returns THAT one with `already_open: true` and 200, so a retry cannot buy a second return label. | 10/min | | `GET /returns` | `shipments:read` | List your returns (paginated, filter by `status`). | 30/min | | `GET /wallet` | `shipments:read` | **The balance `POST /shipments` spends**, plus auto-reload state (`enabled`, `threshold`, `amount`, `card_on_file`, `last_error`) and a ledger page. API purchases settle from the wallet ONLY, so check here before a batch instead of meeting a 402 mid-purchase. Read-only: every top-up rail needs a browser. | 30/min | | `GET /track` | `track:read` | Track a package. Query: `tracking_number`, `carrier`. Returns status, ETA, full checkpoint history. | 60/min | | `GET /pickups` | `pickups:read` | List scheduled pickups (statuses refreshed against the carrier). | 30/min | | `DELETE /pickups/{id}` | `pickups:write` | Cancel a scheduled pickup. Idempotent. | 10/min | | `POST /addresses/validate` | `addresses:read` | **Check an address before buying a label for it.** Returns a confidence score plus corrections. Send `addresses: []` (max 500) for a batch (batch runs format rules only, no carrier lookup). | 60/min | | `GET /addresses` | `addresses:read` | List saved address book entries (paginated). | 30/min | | `POST /addresses` | `addresses:write` | Create an address. | 30/min | | `GET /addresses/{id}` | `addresses:read` | Get one address. | 30/min | | `PATCH /addresses/{id}` | `addresses:write` | Update an address. | 30/min | | `DELETE /addresses/{id}` | `addresses:write` | Delete an address. | 30/min | **Buying a label is a two-call flow:** `GET /rates` (or `POST /rates` for several parcels) → take a `rate_id` → `POST /shipments`. Because the price rides inside the signed `rate_id`, an integration cannot be charged a price it was not quoted. Labels are minted asynchronously by most carriers, so `label_url` is often `null` in the create response, so poll `GET /shipments/{id}` until it is set. 🔴 **Anything that costs extra must be priced into the QUOTE, not added at purchase.** `rate_id` is signed, so the price is fixed the moment it is issued: - **Insurance**: send `insured_value` (the declared value to cover, 0-5000) on `GET /rates`. The rate comes back with `insurance_fee` (the premium, additive to `total_charge`) and `insurance_coverage` (what it covers, after the clamp). Then send `insurance: true` on `POST /shipments`. **Both halves are required**: `insurance: true` against a rate quoted WITHOUT `insured_value` charges 0 and covers 0. - **Signature on delivery**: send `signature_required=true` on the quote AND on the purchase. - **Box kind**: `package_type` (`package` | `envelope` | `courier_pak`). Envelopes and courier paks are cheaper. Signed into `rate_id` with the dimensions, so quoting one kind and shipping another is refused with a 400. The COVERAGE amount rides inside `rate_id`: you send `insurance: true` on the purchase and nothing else, so the amount insured cannot disagree with the premium billed. `insured_value` is COVERAGE, not the premium: insuring a $100 parcel costs a dollar or two, not $100. `declared_value` on the parcel is the customs declaration and is a separate field; keep the two equal unless you have a reason not to. - **Multiple parcels**: quote them together with `POST /rates`. `rate_id` is signed against the PARCELS as well as the price, and `POST /shipments` recomputes that fingerprint from your body: send different boxes than you quoted and the purchase is refused with a 400 rather than billed at the quoted price. Order does not matter. A `rate_id` from `GET /rates` covers one parcel, so a multi-parcel body against one of those is still refused; quote with `POST /rates` instead. **Webhooks that fire:** `shipment.created`, `shipment.label_ready`, `shipment.status_changed`, `shipment.delivered`, `return.created` (all five). `shipment.label_ready` carries the `label_url` and is delivered **exactly once per shipment**, so it is the event to drive "label ready → print it" from; polling `GET /shipments/{id}` remains a valid fallback. `return.created` fires once per return and carries `return_label_url` when the carrier could mint one. **International (`POST /shipments`):** `incoterm` is `DDU` by default: the recipient is billed duty on delivery. Send `incoterm: "DDP"` to prepay it (added to the wallet debit and the invoice), and `fta_claimed: true` to claim CUSMA/USMCA, which can zero the duty on qualifying goods. MesColis prices the duty server-side (Easyship on US/CN lanes, Zonos on Canada Post CA→US, a tariff engine elsewhere) and ships **DDU** whenever it could not price or collect it, so a DDP request never leaves a duty unpaid. **Pickup:** `handover: "pickup"` books a courier only when `pickup_date` is also sent. The fee ($2.50, or $3.50 for Canada Post) is derived server-side and only charged when a date is present. **Validate before you buy.** A bad address does not fail at purchase. It fails days later as a delivery exception, a return leg and a second label. `POST /addresses/validate` is the cheapest place to catch it. `verified_against_carrier: true` means the address was confirmed to exist; `false` means only format rules ran, so `valid: true` there means "well-formed", not "deliverable". **Undo is a real operation.** `POST /shipments/{id}/cancel` is the only way to stop paying for a label you should not have bought; there is no automatic expiry. Cancel while the parcel is still with you; once the carrier collects it or the payment captures, the window closes for good. **Operational prerequisite:** API purchases settle from the **prepaid wallet** only. Card, PayPal and crypto all require a payment authorised in a browser before the call, which an unattended integration cannot do. Fund the wallet and enable auto-reload. ### Pagination List endpoints accept `limit` (max 100) and `offset`. Responses use a standard envelope: ```json { "object": "list", "data": [ ], "count": 142, "limit": 20, "offset": 0, "has_more": true } ``` ### Example requests ```bash # Compare rates curl "https://www.mescolis.ca/api/v1/rates?from_postal=H2X1Y1&to_postal=M5V2T6&weight_kg=1.5&length_cm=30&width_cm=20&height_cm=15" \ -H "Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Compare rates for a multi-box shipment (up to 10 parcels, one label) curl -X POST "https://www.mescolis.ca/api/v1/rates" \ -H "Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"from":{"postal":"H2X1Y1"},"to":{"postal":"M5V2T6"}, "packages":[{"weight_kg":1.5,"length_cm":30,"width_cm":20,"height_cm":15}, {"weight_kg":2.4,"length_cm":40,"width_cm":25,"height_cm":18}]}' # Buy a label with a rate_id from the call above curl -X POST "https://www.mescolis.ca/api/v1/shipments" \ -H "Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \ -d '{"rate_id":"","reference":"ORDER-1042", "from":{"name":"Warehouse","address":"1500 Bd Saint-Martin O","city":"Laval","province":"QC","postal":"H7S 1M9","country":"CA","phone":"5145550142","email":"shipping@example.com"}, "to":{"name":"Marie Tremblay","address":"425 Rue Sainte-Catherine E","city":"Montreal","province":"QC","postal":"H2L 2C4","country":"CA","phone":"5145550188","email":"marie@example.com","residential":true}, "packages":[{"weight_kg":1.2,"length_cm":25,"width_cm":18,"height_cm":10,"description":"Refurbished phone","declared_value":320}]}' # List shipments curl "https://www.mescolis.ca/api/v1/shipments" \ -H "Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # Track a package curl "https://www.mescolis.ca/api/v1/track?tracking_number=1Z999AA10123456784&carrier=ups" \ -H "Authorization: Bearer mc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` ### Errors All errors: `{ "error": "Human-readable description" }`. | Status | Meaning | |--------|---------| | 400 | Bad request: missing/invalid parameter | | 401 | Missing or invalid API key | | 403 | API key lacks the required scope | | 404 | Resource not found or belongs to a different account | | 429 | Rate limit exceeded (includes `Retry-After` header, seconds) | | 500 | Unexpected server error. Contact support | ### Webhooks MesColis sends `POST` requests to your registered webhook URL (Settings → Developer Hub) on these events: | Event | Fired when | Status | |-------|-----------|--------| | `shipment.created` | Label purchased; tracking number usually assigned | ✅ emitted | | `shipment.status_changed` | Moved to In Transit / Out for Delivery / Exception | ✅ emitted | | `shipment.delivered` | Carrier reported delivery | ✅ emitted | | `shipment.label_ready` | Label PDF ready | ✅ emitted (exactly once per shipment, carries `label_url`) | | `return.created` | A return is created | ✅ emitted (once per return, carries `return_label_url` when available) | There is no separate exception event: an exception arrives as `shipment.status_changed` with `status: "Exception"`. Delivery is **at-least-once**: 3 attempts (immediate, +2s, +8s); an endpoint failing 10 times consecutively is disabled automatically. Deduplicate on the top-level `id`. Every payload is signed with an `X-MesColis-Signature` header (HMAC-SHA256 of the raw body using your webhook secret). The header value is prefixed `sha256=`, so comparing it against a bare hex digest never matches. Verify before processing: ```js const crypto = require('crypto'); // req.body must be the RAW body: re-serialising JSON changes the digest. const expected = 'sha256=' + crypto.createHmac('sha256', process.env.WEBHOOK_SECRET) .update(req.body).digest('hex'); const sig = req.headers['x-mescolis-signature'] || ''; const ok = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); if (!ok) return res.status(401).send('Invalid signature'); ``` --- ## 8. Common questions - **Free to sign up?** Yes. No credit card required for Starter. - **How does pricing work?** The quote shows charges before confirmation. Carrier adjustments or unpaid import charges can still apply under the service terms. - **Ship outside Canada?** Yes: US, UK, EU, Asia-Pacific. Check required customs documents and eligibility before shipping. - **Use my own carrier accounts?** Pro and Enterprise can attach negotiated carrier accounts (BYOA). - **How are recipients notified?** MesColis handles all customer-facing tracking emails; no carrier emails are sent to your customer. - **API access?** Available on Pro and Enterprise: public REST API, webhooks, API key + scope auth. --- ## 9. Contact - Support: **support@mescolis.ca** (mescolis.ca) · **support@shipsmarter.ca** (shipsmarter.ca) - Bilingual French / English support; response times depend on the request. - Based in Laval (Greater Montréal), Quebec, Canada. Founded 2024.