ClickPost automation — rollout
How to deploy automatic courier assignment and per-order shipment grouping: the pre-migration check, the deploy order, what to verify at each step, and how to turn either feature off again.
Deploying automatic courier assignment and per-order shipment grouping. Both ship off, so the deploy itself changes no behaviour — the ordering below exists so that each step is verifiable before the next one can affect a real shipment.
Source:
admin/shipping-clickpost.mdfor what the features do. This page is only about getting them live safely.
1. Check the database before migrating
DATABASE_URL=<production> bun run db:preflightRead-only. It reports the conditions that would abort the migration, names the offending rows, and exits non-zero so CI can gate on it.
The one likely to fire is duplicate shipment references. That is not a
migration problem — it is evidence of the bug the migration exists to stop: a
reused reference_number is answered by ClickPost with status 323, reported as
success, returning the earlier dispatch's waybill. Any duplicates you find are
shipments holding another shipment's AWB.
Resolve them by deciding which row holds the real waybill and clearing
reference on the others — it is only a record of what was sent. Do not drop
the index to get the migration through; it is the thing that stops this
recurring silently.
2. Migrate
bun run db:migrateTwo migrations apply. Neither rewrites existing rows: columns become nullable, new columns arrive empty, and the check constraints are satisfied by every row written before them (a pre-existing shipment carries a sub-order and no order, which is exactly what "exactly one" requires).
If it fails anyway, bun run db:migrate surfaces the real Postgres error
that drizzle-kit swallows.
3. Deploy
Nothing changes yet — both settings default to off.
If you run split roles, the worker role must be deployed too: the assignment
job runs on a BullMQ queue (clickpost-auto-assign) whose processor only
registers when APP_ROLE is not api. An API-only deploy would enqueue work
nothing consumes.
Confirm the app came up and the queue registered:
redis-cli --scan --pattern 'bull:clickpost-auto-assign*'4. Configure ClickPost
Admin → Settings → Shipping. Automatic assignment needs the platform account fully configured — the same keys manual fulfilment already uses:
clickpost.api_key, clickpost.username, clickpost.webhook_secret,
clickpost.pickup_pincode, the clickpost.pickup_* block, and
clickpost.default_parcel.
Anything missing and assignment records skipped and stays quiet, rather than
failing an order. That is the intended behaviour, but it also means a
half-configured install looks like nothing is happening.
5. Turn on assignment
Set clickpost.auto_assign_enabled = true.
From here, each new order has a courier chosen automatically. Nothing is booked and nothing is billed — an operator still confirms each shipment from the orders table.
Verify on a real order:
- the Courier column shows a carrier shortly after the order is placed
- the shipping tab shows the carrier and its quoted charge
- no AWB exists yet, and the sub-order is still pending
6. Confirm one shipment by hand
Pick a single order and press Confirm shipment. This is the first time anything is billable.
Check: an AWB comes back, the sub-order stays pending, the label prints from
Admin → Print Labels, and the tracking page shows the shipment. Then wait
for the courier's pickup scan and confirm the order flips to fulfilled and the
customer's shipped notification goes out exactly once.
Only once that round-trip works should you use bulk confirm.
7. Per-order grouping (optional)
Only if you pack a multi-vendor order as a single box. Set
shipment_grouping = per_order.
Verify on a multi-vendor order: one shipment, one AWB mirrored onto every bag, one label carrying the full COD amount and every bag's items, and one tracking callback advancing all of them together.
Orders booked before the switch keep their per-bag shipments; both shapes are read correctly, so there is no backfill.
Turning it off
Both settings are live switches, and neither strands anything already in flight.
| Set | Effect |
|---|---|
auto_assign_enabled = false | No new assignments are queued. Existing assignments stay confirmable, and already-booked shipments are unaffected. |
shipment_grouping = per_vendor | New orders go back to one shipment per bag. Already-consolidated parcels keep working — tracking, labels and cancellation all read the shape each shipment was booked in. |
Removing the integration entirely is a one-line change: delete
ClickPostShippingModule.forRoot() from apps/api/src/app.module.ts. The
listener, processor, queue and endpoints all go with it, and the assignment rows
become inert data that nothing reads.
What to watch after enabling
- Assignments stuck at
assigned. Nothing is wrong — they are waiting for someone to confirm. Filter the orders list by Assigned to see the queue. failedassignments. Filter by Assignment failed; the reason is on the row. An unserviceable pincode is a verdict, not an outage, and is not retried.- Sub-orders
pendingwith a live AWB and no movement. The courier has not scanned a pickup. Any later checkpoint fills the gap automatically, but a parcel sitting like this for days means it was never collected.
Settings Module — Admin
HTTP surface for the platform-wide settings store (admin + store scopes) and the platform-admin override surface for vendor-scoped settings (admin + store sub-scopes per vendor).…
Shipping ClickPost Module — Admin surface
Admin-facing HTTP surface for the platform (central warehouse) ClickPost account — credentials, pickup address, courier map, parcel defaults, webhook secret, the live active-courier lookup, and automatic courier assignment. This is the account used when an admin fulfills a sub-order with credentialSource=platform.