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

Developers

API overview

Base URL, authentication, environments, scopes, idempotency, pagination, rate limits and the one error shape.

Merchant developers5 min readPrintable handbook

The QCS REST API is version 1 and lives under https://qcs.com.pk/api/v1. It is JSON in, JSON out, over HTTPS only. Everything a merchant can do in the portal that matters to a shop can be done here: quote, book, label, forward for pickup, track and reconcile.

Getting a key

  1. 01Open the integration portalGo to /m/integrations/keys in the merchant portal. Only a merchant user with the integration permission can issue keys.
  2. 02Choose the environmentSandbox for building, live for real parcels. Keys are not interchangeable and a sandbox key can never move a real parcel.
  3. 03Grant only the scopes you needA key that only quotes rates should carry only the rate scope. Scopes are checked on every request.
  4. 04Copy the secret onceThe secret is shown exactly once, at creation. It is stored hashed with a pepper and cannot be recovered. Rotate the key if you lose it.

Authentication

Send the key id and the secret on every request. Either form is accepted; use the bearer form unless your HTTP client makes custom headers easier.

Authorization header
Bearer <keyId>.<keySecret>
Separate headers
x-qcs-key-id: <keyId>
x-qcs-key-secret: <keySecret>

Environments

Sandbox and live
SandboxLive
Books real parcelsNoYes
ChargedNoYes, on delivery
Appears in operationsNo. Sandbox parcels cannot be picked up, dispatched or delivered.Yes
Rate quotesReal, from your rate cardReal, from your rate card
WebhooksDelivered to your sandbox endpointsDelivered to your live endpoints

Sandbox isolation is enforced in the database, not only in the application: a sandbox parcel cannot be attached to a pickup request, a rider or a delivery run, and cannot move beyond booked or cancelled.

Scopes

Available scopes
ScopeGrants
rates:readQuote a delivery charge
zones:readLook up cities, service areas and addresses
orders:readRead orders and their status
orders:writeBook orders, singly or in bulk
orders:cancelCancel a booked order before pickup
labels:readFetch label and load sheet PDFs
tracking:readRead the public tracking timeline
pickups:readRead pickup requests
pickups:writeRaise a pickup request
statements:readRead the COD ledger and payout statements
webhooks:readRead webhook endpoints and the delivery log
webhooks:manageCreate, edit and replay webhook endpoints

Idempotency

Send idempotency-key on every write. A repeat of the same key with the same body returns the original response instead of booking a second parcel, which is what makes a retry after a timeout safe. A repeat of the same key with a different body is a conflict.

  • Required on order creation, bulk creation and pickup creation.
  • Optional but recommended on cancellation, label requests and webhook writes.
  • Use a value your own system can regenerate for the same order, such as your order number plus the action.

Pagination

List endpoints are cursor-paginated. Pass limit up to 200; the default is 50. The response carries the next cursor, and an absent cursor means you have reached the end. Do not build page numbers; cursors are stable while rows are being inserted and page numbers are not.

Rate limits

Each key carries its own per-minute allowance, set when the key is issued and visible on the key. Exceeding it returns the rate-limited error. Treat it as back-pressure: pause, then retry with the same idempotency key.

Errors

Every failure returns the same envelope. There is no second error shape to handle, and no stack trace, SQL fragment or internal identifier ever appears in it.

Error envelope
{
  "error": {
    "code": "QCS_VALIDATION_FAILED",
    "message": "Enter the parcel weight in kilograms, up to three decimal places, such as 1.25.",
    "details": { "fieldErrors": { "weightKg": ["Enter the parcel weight in kilograms."] } },
    "requestId": "req_01hz..."
  }
}
Error codes
CodeHTTPWhat it means
QCS_VALIDATION_FAILED422The payload failed validation. details names the field and the reason.
QCS_AUTH_REQUIRED401The key or the secret is missing, wrong, expired or revoked.
QCS_PERMISSION_DENIED403The key is valid but does not carry the scope this endpoint needs.
QCS_NOT_FOUND404No record of yours matches that reference. A parcel belonging to another merchant reads as not found, never as forbidden.
QCS_TRANSITION_REJECTED409The parcel cannot move that way from where it is. Read the current status and retry the right action.
QCS_CONFLICT409A uniqueness or concurrency conflict, such as reusing an idempotency key with a different body.
QCS_RATE_LIMITED429The key exceeded its per-minute allowance. Back off and retry.
QCS_UNEXPECTED500Something failed on our side. requestId identifies the request in our logs; quote it to support.

Money, weight and time

  • Money on the wire is whole rupees, as a string or a number. QCS stores paisa internally and never uses floating point for money.
  • Weight on the wire is kilograms with up to three decimals. Dimensions are centimetres with up to one decimal.
  • Timestamps are ISO 8601 in UTC. Operational dates are Asia/Karachi calendar dates in YYYY-MM-DD form.

Next