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, and the live active-courier lookup. This is the account used when an admin fulfills a sub-order with credentialSource=platform.
Admin-facing HTTP surface for the platform (central warehouse) ClickPost account — credentials, pickup address, courier partner map, parcel defaults, webhook secret, and the live active-courier lookup. This is the account used when an admin fulfills a sub-order with credentialSource=platform; sellers shipping under their own ClickPost account configure the mirror surface in vendor/shipping-clickpost.md.
Source:
api-modules/shipping-clickpost/src/controllers/admin-clickpost-config.controller.ts.The endpoints are a typed wrapper over the
admin.shipping.clickpost.*settings group. They exist as a dedicated surface becauseclickpost.courier_mapis a record of objects — the generic settings editor can edit an existing value but cannot add keys to it.
Conventions
Authentication
All endpoints require a Better-Auth admin session and a role granting the matching permission. Uses the adminSetting resource — the same resource that gates the underlying settings group, so no separate grant is needed to manage ClickPost.
| Endpoint | Permission |
|---|---|
GET /admin/shipping/clickpost/config | adminSetting: read |
GET /admin/shipping/clickpost/active-couriers | adminSetting: read |
PATCH /admin/shipping/clickpost/config | adminSetting: update |
Response envelope
{
"data": <payload>,
"message": "Success",
"statusCode": 200,
"metadata": { /* optional */ }
}Error envelope
statusCode | errorCode examples |
|---|---|
| 400 | BAD_REQUEST, VALIDATION_ERROR |
| 401 | UNAUTHORIZED |
| 403 | FORBIDDEN |
| 500 | INTERNAL_SERVER_ERROR |
Domain types
Identical to the vendor surface — see vendor/shipping-clickpost.md for the full field-by-field notes on ClickPostCourierEntry, weights/dimensions units, and where to obtain cpId / accountCode.
ClickPostConfigResponse
type ClickPostConfigResponse = {
apiKey: string;
username: string;
webhookSecret: string;
pickupPincode: string; // Indian 6-digit
enabledCouriers: string[];
courierMap: Record<string, { cpId: number; accountCode: string }>;
pickupName: string;
pickupAddress: string;
pickupCity: string;
pickupState: string;
pickupPhone: string;
pickupEmail: string;
pickupTin: string; // TIN / GSTIN
defaultParcel: {
weight: number; // grams
length: number; // centimetres
breadth: number;
height: number;
} | null;
/** Computed from PUBLIC_API_BASE_URL + the platform webhook route.
* Null when PUBLIC_API_BASE_URL isn't set on the API process. */
webhookUrl: string | null;
};Unlike the vendor variant, webhookUrl resolves to the platform route (/webhooks/shipping/clickpost/platform), not a per-vendor one.
ActiveCourier
type ActiveCourier = {
code: string; // our internal courier code, e.g. "DELHIVERY"
courierName: string; // name as shown on the ClickPost dashboard
cpId: number;
accountCode: string;
};Platform ClickPost config
Base path: /admin/shipping/clickpost.
GET /admin/shipping/clickpost/config — Get config
Returns the platform warehouse ClickPost credentials, pickup address, courier map, parcel defaults, and the resolved platform webhook URL.
Response 200 — ClickPostConfigResponse.
{
"data": {
"apiKey": "cp_live_...",
"username": "acme-warehouse",
"webhookSecret": "whsec_...",
"pickupPincode": "560001",
"enabledCouriers": ["DELHIVERY", "BLUEDART"],
"courierMap": {
"DELHIVERY": { "cpId": 4, "accountCode": "DL_ACME_002" },
"BLUEDART": { "cpId": 12, "accountCode": "BD_ACME_001" }
},
"pickupName": "Acme Central Warehouse",
"pickupAddress": "123 Industrial Area",
"pickupCity": "Bengaluru",
"pickupState": "Karnataka",
"pickupPhone": "9876543210",
"pickupEmail": "dispatch@acme.com",
"pickupTin": "29ABCDE1234F1Z5",
"defaultParcel": { "weight": 500, "length": 20, "breadth": 15, "height": 10 },
"webhookUrl": "https://api.example.com/webhooks/shipping/clickpost/platform"
},
"message": "Success",
"statusCode": 200
}Errors
| Status | Code | When |
|---|---|---|
| 403 | FORBIDDEN | Caller lacks adminSetting:read |
GET /admin/shipping/clickpost/active-couriers — Active couriers
Calls ClickPost's fetch-accounts API with the saved platform API key and returns only the active accounts whose courier name matches a platform-supported code (CLICKPOST_SUPPORTED_COURIERS), each with its cpId and accountCode. The config UI uses this to offer only couriers that will actually work and to prefill the courier map.
Returns an empty array — never an error — when the API key isn't saved yet or ClickPost is unreachable, so an optional lookup never breaks the page.
Response 200 — ActiveCourier[].
{
"data": [
{
"code": "DELHIVERY",
"courierName": "Delhivery",
"cpId": 4,
"accountCode": "DL_ACME_002"
}
],
"message": "Success",
"statusCode": 200
}Errors
| Status | Code | When |
|---|---|---|
| 403 | FORBIDDEN | Caller lacks adminSetting:read |
PATCH /admin/shipping/clickpost/config — Update config
Partial update — fields not in the body are left untouched. The body is .strict() so unknown keys are rejected at the zod layer. Each field maps to a setting under admin.shipping.clickpost.* and is written via SettingsService.setMany (audit row per changed key, one transaction).
Body — same shape as the vendor PATCH:
{
"apiKey": "cp_live_...",
"username": "acme-warehouse",
"webhookSecret": "whsec_...",
"pickupPincode": "560001",
"enabledCouriers": ["DELHIVERY", "BLUEDART"],
"courierMap": {
"DELHIVERY": { "cpId": 4, "accountCode": "DL_ACME_002" },
"BLUEDART": { "cpId": 12, "accountCode": "BD_ACME_001" }
},
"pickupName": "Acme Central Warehouse",
"pickupAddress": "123 Industrial Area",
"pickupCity": "Bengaluru",
"pickupState": "Karnataka",
"pickupPhone": "9876543210",
"pickupEmail": "dispatch@acme.com",
"pickupTin": "29ABCDE1234F1Z5",
"defaultParcel": { "weight": 500, "length": 20, "breadth": 15, "height": 10 }
}| Field | Type | Constraints | Setting key |
|---|---|---|---|
apiKey | string? | Trimmed; 1..500 chars | clickpost.api_key |
username | string? | Trimmed; 1..200 chars | clickpost.username |
webhookSecret | string? | Trimmed; 32..500 chars | clickpost.webhook_secret |
pickupPincode | string? | Trimmed; exactly 6 digits (/^\d{6}$/) | clickpost.pickup_pincode |
enabledCouriers | string[]? | Each entry non-empty | clickpost.enabled_couriers |
courierMap | Record<string, { cpId: number; accountCode: string }>? | cpId positive int; accountCode 1..120 chars | clickpost.courier_map |
pickupName | string? | Trimmed; 1..120 chars | clickpost.pickup_name |
pickupAddress | string? | Trimmed; 1..500 chars | clickpost.pickup_address |
pickupCity | string? | Trimmed; 1..120 chars | clickpost.pickup_city |
pickupState | string? | Trimmed; 1..120 chars | clickpost.pickup_state |
pickupPhone | string? | Trimmed; 6..20 chars | clickpost.pickup_phone |
pickupEmail | string? | Valid email, max 200 chars | clickpost.pickup_email |
pickupTin | string? | Trimmed; 1..40 chars | clickpost.pickup_tin |
defaultParcel | { weight, length, breadth, height }? | All four positive integers; weight in grams, rest in cm. Required in practice — see below | clickpost.default_parcel |
defaultParcelis not optional in practice.getCredentialsrejects a config without it, andbuildCreateOrderPayloadsendslength/breadth/heightfrom it on every shipment — ClickPost's v3 payload carries no per-item dimensions, so this is the only source. Onlyweightbehaves as a true fallback (item.weight ?? defaultParcel.weight).
enabledCouriersgates nothing on the server. It drives the config UI's courier picker, but no code path validates a shipment against it:resolveCourierandmethodsForPlatformboth key offcourierMap. A courier is usable when it has acourierMapentry, whether or not it appears inenabledCouriers.
Response 200 — updated ClickPostConfigResponse (re-read after the write).
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Body fails zod, or a value fails its registry schema |
| 403 | FORBIDDEN | Caller lacks adminSetting:update |
Fulfillment flow
When an admin marks a sub-order fulfilled via POST /admin/orders/vendors/:orderVendorId/fulfilled with providerId="clickpost" and credentialSource="platform", ClickPostShippingProvider.createShipment reads these settings instead of the vendor's. credentialSource="vendor" reads the seller's own vendor.admin.shipping.clickpost.* keys.
GET /admin/orders/vendors/:orderVendorId/fulfillment-options returns both accounts with their selectable couriers; the platform account's list is the intersection of CLICKPOST_SUPPORTED_COURIERS with the keys of the courierMap configured here — so a courier with no mapping simply cannot be picked.
The remaining mechanics (v3 payload construction, async 102/202 handling, variant weight/dimension fallbacks) are identical to the vendor flow — see vendor/shipping-clickpost.md.
Webhook receiver
Tracking events for platform-fulfilled bags arrive at POST /webhooks/shipping/clickpost/platform, HMAC-verified against admin.shipping.clickpost.webhook_secret and resolved by AWB globally (not vendor-scoped). See webhooks/shipping-clickpost.md.
Related modules
shipping— provider-agnostic config;clickpostmust appear inadmin.shipping.enabled_providers. Seeadmin/shipping.md.settings—admin.shipping.clickpost.*keys live in the admin settings registry under theshippinggroup; these endpoints are a typed wrapper. Seeadmin/settings.md.order— admin fulfillment dispatch and thecredentialSourcechoice. Seeadmin/order.md.admin-rbac— gates every endpoint viaadminSetting:*. Seeadmin/admin-rbac.md.
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 Module — Admin
HTTP surface for the platform-admin override of any vendor's shipping config (enabled providers list + customer-charge flat-rate + free-shipping threshold). Vendor-self read/write…