Skip to main content
QCSQuick Courier ServiceQCS — Quick Courier Service, home

Store connectors

Custom API wizard

Push an order from any system in whatever shape it already has, and tell QCS where to read each value.

Developers integrating an in-house or unsupported platform4 min readPrintable handbook

If your system can post JSON but cannot be rewritten to match somebody else’s shape, use the custom connection. You post your own payload, and a field map tells QCS which path in that payload holds each value it needs.

When to use this instead of the API

The REST API at /api/v1/orders is the better choice when you control the request. The custom connection exists for systems that can only fire a fixed webhook: an ERP, a POS, a legacy order manager, or a no-code automation.

Setting it up

  1. 01Create the connectionOpen /m/integrations/custom. QCS gives you a connection key and shows the signing secret once.
  2. 02Post a sample payloadSend one real order to the push endpoint, or paste it into the wizard. QCS reads every path in it and offers them in the mapping editor.
  3. 03Map the fieldsFor each QCS field, pick the path in your payload. Paths are dotted, with array indexes, such as shipping.address.line1 or items[0].weight.
  4. 04Validate and go liveThe wizard prices the mapped order and shows you the parcel it would create. When that looks right, switch the connection live.

The endpoint

Where to post
PurposeEndpoint
Order pushhttps://qcs.com.pk/api/webhooks/custom/orders
Required headers
HeaderValue
x-qcs-connectionYour connection key.
x-qcs-signaturet=<unix seconds>,v1=<64 hex characters>
Content-Typeapplication/json

The signature is HMAC-SHA256 over the unix timestamp, a full stop, and the exact raw body, using your connection secret. Sign the bytes you send, not a re-serialised copy. QCS allows five minutes of clock drift.

Signing in Node
import { createHmac } from 'node:crypto'

const body = JSON.stringify(order)
const timestamp = Math.floor(Date.now() / 1000)
const digest = createHmac('sha256', process.env.QCS_CONNECTION_SECRET)
  .update(`${timestamp}.${body}`, 'utf8')
  .digest('hex')

await fetch('https://qcs.com.pk/api/webhooks/custom/orders', {
  method: 'POST',
  headers: {
    'x-qcs-connection': process.env.QCS_CONNECTION_KEY,
    'x-qcs-signature': `t=${timestamp},v1=${digest}`,
    'Content-Type': 'application/json',
  },
  body,
})

The fields QCS needs

Mapping targets
FieldRequiredWhat it is
Your order idRequiredThe value QCS stores so the same order is never imported twice.
Your order numberOptionalThe human reference your team uses, such as 1042.
Payment methodOptionalSend cod for cash on delivery, or anything else for a prepaid parcel.
Customer nameRequiredWho the rider asks for at the door.
Customer mobileRequiredA Pakistani mobile such as 03001234567. The rider calls this number.
Second mobileOptionalUsed when the first number does not answer.
Customer emailOptionalUsed for email tracking updates.
AddressRequiredHouse or shop number, street and area, as one line of at least ten characters.
Area or sectorOptionalHelps QCS place the parcel in the right zone.
LandmarkOptionalA nearby point the rider can find quickly.
City nameOptionalMatched against the cities QCS serves.
LatitudeOptionalSend it when your checkout already has a pin.
LongitudeOptionalSend it with the latitude, never on its own.
PiecesOptionalHow many physical parcels. Defaults to one.
Weight in kilogramsOptionalUp to three decimals, such as 1.25. Falls back to the connection default.
Length in centimetresOptionalSend length, width and height together.
Width in centimetresOptionalSend length, width and height together.
Height in centimetresOptionalSend length, width and height together.
What is insideOptionalShown on the label, up to 200 characters.
Declared valueOptionalIn rupees. Used for claims, never for pricing.
Cash to collectOptionalIn rupees. Leave unmapped for a prepaid parcel.
Order totalOptionalIn rupees. Used as the cash amount when no COD field is mapped.
Delivery noteOptionalPassed to the rider as a special instruction.

Use a dotted path into your payload, such as customer.phone or items[0].sku.

Reading the result

The response carries the tracking number and the frozen charge. Every push, accepted or rejected, is recorded at /m/integrations/logs with your order id, the mapped values and the reason for a rejection.

Questions we get asked

My payload nests the address three levels deep.That is fine. Paths may be up to eight segments deep and may index arrays. The wizard lists every path it found in your sample, so you can pick rather than type.
My weights are in grams.Map the field and tell the wizard it is grams. QCS stores grams internally, so no precision is lost either way.
Can I also receive status changes?Yes. Create a webhook endpoint at /m/integrations/webhooks. The push connection is one direction only.

Next