Order API Reference
Complete API contracts for the Order module, including routes, auth, DTOs, responses, errors, examples, and integration notes.
Order - API Reference
Audience: Frontend engineers, mobile engineers, backend engineers, QA, and API consumers. Scope: The 28 order routes — 8 customer, 11 order admin, 5 return admin, 4 refund admin.
1. Documentation Evidence
| Area | Files Inspected | What Was Verified |
|---|---|---|
| Controllers | apps/api/src/modules/order/customer/order-customer.controller.ts, admin/{order,return,refund}/*.controller.ts | Routes, methods, guards, permissions |
| DTOs | dto/*.ts | Validation |
| Services | customer/admin/return/refund services | Behavior, transitions |
| Schema | packages/db/src/schema/order/*.ts | 12 tables, CHECKs |
| Error registry | apps/api/src/common/types/error-codes.ts (// ORDER) | ORDER_* codes |
2. Module Summary
| Field | Value |
|---|---|
| Module name | order |
| Module slug | order |
| Primary actors | customer, admin (orders/returns/refunds), worker |
| API surfaces | mobile (customer), admin |
| Base route prefixes | /api/mobile/orders, /api/orders, /api/order-returns, /api/order-refunds |
| Auth model | JwtAuthGuard (customer); JwtAuthGuard + RoleGuard (admin) |
| Persistence | PostgreSQL (12 tables); no cache |
| Runtime source of truth | Order rows (snapshots) + live inventory for restock |
| Sibling docs | Backend, Features and flows |
3. Concepts and Terminology
| Term | Meaning | Source File | Used By |
|---|---|---|---|
status | One of nine order statuses — none returned/refunded | schema | All routes |
Per-line label | Derived display label; quantities are the truth | response builder | Order responses |
unavailableQuantity | A short order — paid for, not coming | schema | Short orders |
codCollectedAt | COD money actually changed hands | schema | COD flows |
returnPendingQuantity | Units in a return in flight | schema | Returns |
pollAfterSeconds | Server-set poll interval | status endpoint | Polling |
refundedAmount | Denormalised counter, CHECK-bounded | schema | Refunds |
4. API Surface Map
4.1 Customer — /api/mobile/orders
| Method | Path | Purpose |
|---|---|---|
GET | / | My orders |
GET | /:id | One order |
GET | /:id/status | Small poll payload |
GET | /:id/invoice.pdf | Receipt PDF |
POST | /:id/cancel | Cancel before dispatch |
POST | /:id/returns | Request a return |
GET | /:id/returns | My returns on this order |
POST | /:id/returns/:returnId/cancel | Cancel a pending return |
4.2 Admin — orders, /api/orders
| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | / | Orders_READ | List |
GET | /:id | Orders_READ | Detail |
POST | /:id/confirm | Orders_UPDATE | Confirm |
POST | /:id/processing | Orders_UPDATE | Processing |
POST | /:id/cancel | Orders_UPDATE | Cancel |
POST | /:id/notes | Orders_UPDATE | Add note |
POST | /:id/cod-collection | Orders_UPDATE | Record COD cash |
POST | /:id/shipments | Orders_UPDATE | Dispatch (with serials) |
POST | /:id/shipments/:shipmentId/deliver | Orders_UPDATE | Deliver parcel |
POST | /:id/shipments/:shipmentId/fail | Orders_UPDATE | Fail parcel |
POST | /:id/refunds | Refunds_UPDATE | Create refund |
POST | /:id/ready-for-pickup | Orders_UPDATE | Tell the customer a pickup order is waiting |
POST | /:id/collect | Orders_UPDATE | Hand a pickup order over — drives it to delivered |
The last two are the pickup lifecycle, and they sit on the same /api/orders prefix as the rest
even though they live in their own controller: readiness and collection are one lifecycle, share
two error codes, and neither means anything for a delivery order. Both refuse a delivery order
with ORDER_NOT_PICKUP.
/collect is the pickup counterpart of /:id/shipments and the only thing that moves a pickup
order to delivered — until it runs, the customer cannot review an item or open a return, because
the return window opens on delivery. It is deliberately not idempotent (ORDER_NOT_COLLECTABLE
on a second press): at a counter, pressing twice is far more likely a double-tap than a retry.
/ready-for-pickup is the opposite — pressing it again re-enters the same state, and the outbox
dedupe key collapses the repeats into one email.
Two operators pressing "ready" at once no longer 409s. The endpoint moves a confirmed order to
processing first, and the second request lost that race: it hit ORDER_TRANSITION_NOT_ALLOWED
and showed the operator a failure for an order that was, at that moment, exactly as ready as they
had asked for. Two people at one counter is the normal case for this route, not an edge case.
It now tolerates precisely that code from the transition — and only that code — then re-reads the order and continues. Any other failure still propagates. The order still ends in the state the caller asked for, which is the test an idempotent endpoint has to pass; a transition genuinely refused (already collected, cancelled) is a different state and still refuses.
4.3 Admin — returns, /api/order-returns
| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | / | Returns_READ | List |
GET | /:id | Returns_READ | Detail |
POST | /:id/decide | Returns_UPDATE | Approve/reject |
POST | /:id/receive | Returns_UPDATE | Goods back |
POST | /:id/inspect | Returns_UPDATE | Inspect (quantities + reason) |
4.4 Admin — refunds, /api/order-refunds
| Method | Path | Permission | Purpose |
|---|---|---|---|
GET | / | Refunds_READ | List |
POST | /:id/approve | Refunds_UPDATE | Approve (choose method) |
POST | /:id/reject | Refunds_UPDATE | Reject (pending only) |
POST | /:id/settle | Refunds_UPDATE | Settle (reference required) |
5. Auth, Identity, and Permissions
| Surface | Guard/Decorator | Identity Shape | Permission | Guest Allowed | Notes |
|---|---|---|---|---|---|
| Customer | JwtAuthGuard | req.user.id | — | No | Ownership-scoped: another customer's order is 404 |
| Admin | JwtAuthGuard, RoleGuard | req.user | Orders_* / Returns_* / Refunds_* | No | Three separate permission modules — money-back is never a side effect of dispatch rights; run permissions:sync on deploy |
6. DTO and Model Reference
6.1 Create refund — POST /api/orders/:id/refunds
The caller sends lines and quantities — the server computes the money. No amount field.
{ "items": [ { "orderItemId": "…", "quantity": 1 } ] }6.2 Approve refund
{ "method": "bank_transfer" }The method is chosen at approval — the row has no method before this. Values: esewa_reversal / bank_transfer / cash.
6.3 Settle refund
{ "settlementReference": "BRN-12345" }Required by constraint — 400 ORDER_REFUND_SETTLEMENT_REFERENCE_REQUIRED without it.
6.4 Return DTOs
Decide: approval state (+ reason when rejecting). Inspect: per-item quantities and, when rejecting, a reason (ORDER_RETURN_INSPECTION_INVALID names which).
6.5 Query DTOs
Order list and return/refund lists follow the standard QueryDto paging with filters (status, date range, customer, etc.); the refund list is the finance queue.
6.6 Order line item — the frozen configuration
Every order line was frozen from a checkout line that names a product_variant, not only a
product. Both the customer and admin order-item response DTOs expose which configuration was
bought — added because order_item had carried the columns since migration 0028 with no surface
exposing either, so a customer's order history showed "iPhone 17 Pro" with no way to tell the
256GB they bought from the 1TB they did not.
| Surface | DTO | Identity field | Notes |
|---|---|---|---|
Customer (GET /:id, list) | OrderItemResponseDto | variantPublicId (uuid, not nullable) | product_variant.public_id, frozen at purchase |
Admin (GET /api/orders/:id, list) | OrderItemAdminDto | variantId (uuid, not nullable) | Same value, different field name — the admin DTO names it variantId even though it carries the variant's PUBLIC id, not the integer PK |
Both DTOs also carry variantName: string \| null. variantName is null exactly when the
variant was the product's sole configuration and carried no label of its own — render the
product name alone in that case, never the word "null", and never a label re-derived from the
variant's CURRENT name, which would rewrite what was actually bought. Both DTOs additionally
carry productPublicId/productId, productName, sku, unitPrice, mrp, quantity,
lineTotal and discountAmount — all in integer minor units.
7. Enum Reference
| Enum | Value | Meaning | Runtime Effect | Source |
|---|---|---|---|---|
order_status | created / confirmed / processing / partially_shipped / shipped / partially_delivered / delivered / completed / cancelled | The order | Nine values — none returned or refunded | enums.ts |
order_refund_method | esewa_reversal / bank_transfer / cash | How money goes back | Chosen at approval | |
| per-line label (derived) | unavailable / cancelled / delivered / partially_delivered / shipped / partially_shipped / processing | Display | Quantities are the truth | response builder |
8. Endpoint Reference
8.1 GET /api/mobile/orders
My purchase history. ORDER_NOT_FOUND semantics apply per row (no existence oracle).
8.2 GET /api/mobile/orders/:id
Full order: status, per-line quantities + derived label, the frozen variantPublicId/variantName per line (§6.6), pricing (minor units), shipment/return/refund summaries, the customer-visible timeline.
8.3 GET /api/mobile/orders/:id/status
Small payload for polling: order status, per-line labels, the latest customer-visible timeline entry, pollAfterSeconds. Honour pollAfterSeconds — it can be lengthened server-side under load without shipping a frontend. Reads recorded state only; cheap.
8.4 GET /api/mobile/orders/:id/invoice.pdf
Streams a receipt PDF, generated on demand, carrying verbatim and prominently: "This is a sales receipt for your records. It is NOT an official taxable invoice — no VAT or PAN registration is claimed." Never label it "Tax Invoice".
8.5 POST /api/mobile/orders/:id/cancel
Cancel before dispatch. Restock happens in the same transaction; a restock refusal rolls the cancellation back (ORDER_RESTOCK_REFUSED, 500, nothing changed). COD without codCollectedAt refunds nothing — the cancellation email says so.
Error Cases
| HTTP | Code | Condition |
|---|---|---|
| 404 | ORDER_NOT_FOUND | Unknown or another customer's |
| 409 | ORDER_TRANSITION_NOT_ALLOWED | Order moved |
| 409 | ORDER_NOT_CANCELLABLE | Already dispatched or delivered — offer a return (message says which) |
| 500 | ORDER_RESTOCK_REFUSED | Inventory declined; rolled back |
8.6 POST /api/mobile/orders/:id/returns (+ GET, + cancel)
Request a return on delivered lines within the window. ORDER_RETURN_NOT_ELIGIBLE (409, date in message) when not delivered or past the window; ORDER_RETURN_QUANTITY_UNAVAILABLE (409) on a double-tap — units already in a return; refresh. Cancel releases the budget.
8.7 Admin order routes — POST /api/orders/:id/{confirm,processing,notes,cod-collection,shipments,shipments/:shipmentId/{deliver,fail}}
Transitions guarded by ORDER_TRANSITION_NOT_ALLOWED. Shipments take quantities + serial numbers — ORDER_SERIAL_COUNT_MISMATCH (more serials than units), ORDER_SERIAL_ALREADY_RECORDED (duplicate device — operator check). cod-collection is only applicable for COD, delivered, not already counted (ORDER_COD_COLLECTION_NOT_APPLICABLE). Deliver/fail close a parcel (ORDER_SHIPMENT_NOT_OPEN if already closed).
8.8 POST /api/orders/:id/refunds
Create a refund from order lines — server computes the money from the net ceilings. ORDER_REFUND_EXCEEDS_PAID (409) if it would exceed what is left to give back — reachable with no operator fault (two people approving at once); say the figure changed and refresh.
8.9 Return admin — /api/order-returns/:id/{decide,receive,inspect}
ORDER_RETURN_TRANSITION_NOT_ALLOWED on wrong-state steps; ORDER_RETURN_INSPECTION_INVALID names which quantities or the missing rejection reason.
8.10 Refund admin — /api/order-refunds/:id/{approve,reject,settle}
- approve — choose the method (DTO per §6.2).
- reject —
pendingonly; an approved refund can never be rejected (ORDER_REFUND_TRANSITION_NOT_ALLOWED). - settle — settlement reference required (§6.3). This is where a human records that money actually moved; no gateway refund is integrated.
9. Flow Diagrams
9.1 Route Ownership
9.2 Request Sequence (cancel)
9.3 Error Branch (refund settle)
10. Pagination, Sorting, Filtering, and Search
| Endpoint | Pagination Type | Default Size | Max Size | Sort Fields | Filters |
|---|---|---|---|---|---|
GET /api/mobile/orders | offset page/size | 20 | 100 | standard | status, date |
GET /api/orders | offset page/size | 20 | 100 | standard | status, customer, date, totals |
GET /api/order-returns | offset page/size | 20 | 100 | standard | status, order |
GET /api/order-refunds | offset page/size | 20 | 100 | standard | status — the finance queue |
11. Caching, Jobs, and External Integrations
| Integration | Used? | Details |
|---|---|---|
| Redis cache | No | Status reads recorded state only |
| BullMQ | Yes | ORDER queue — CREATE_ORDER from the outbox row + email jobs |
| Yes (optional key) | RESEND_API_KEY optional; without it emails render/queue and are logged instead of delivered | |
| Gateway refund | No | Settlement is human; reference required by constraint |
13. Mandatory Deep API Documentation Pack
13.1 Route-by-Route Completeness Matrix
| Route | Controller Method | Service Method | Guards | Permissions | DB Touches | Errors | Documented? |
|---|---|---|---|---|---|---|---|
GET /api/mobile/orders | findAll | OrderCustomerService.list | JWT | — | orders | — | Yes |
GET /api/mobile/orders/:id | findById | …findOne | JWT | — | order + lines | 404 | Yes |
GET /:id/status | status | …status | JWT | — | order | 404 | Yes |
GET /:id/invoice.pdf | invoice | …invoice | JWT | — | order | 404 | Yes |
POST /:id/cancel | cancel | …cancel | JWT | — | order + restock | 404/409/500 | Yes |
POST /:id/returns | createReturn | return service | JWT | — | order, items | 404/409 | Yes |
GET /:id/returns | listReturns | — | JWT | — | returns | 404 | Yes |
POST /:id/returns/:returnId/cancel | cancelReturn | return service | JWT | — | return | 404/409 | Yes |
GET /api/orders | findAll | OrderAdminService.list | JWT+Role | Orders_READ | orders | — | Yes |
GET /api/orders/:id | findById | …findOne | JWT+Role | Orders_READ | order | 404 | Yes |
POST /:id/{confirm,processing,cancel,notes,cod-collection} | 5 methods | admin service | JWT+Role | Orders_UPDATE | order + events | 404/409 | Yes |
POST /:id/shipments | ship | admin service | JWT+Role | Orders_UPDATE | shipment + serials | 404/409 | Yes |
POST /:id/shipments/:shipmentId/{deliver,fail} | 2 methods | admin service | JWT+Role | Orders_UPDATE | shipment | 404/409 | Yes |
POST /api/orders/:id/refunds | createRefund | refund service | JWT+Role | Refunds_UPDATE | refund rows | 404/409 | Yes |
GET /api/order-returns / /:id | 2 methods | return service | JWT+Role | Returns_READ | returns | 404 | Yes |
POST /:id/{decide,receive,inspect} | 3 methods | return service | JWT+Role | Returns_UPDATE | return rows | 404/409 | Yes |
GET /api/order-refunds | findAll | refund service | JWT+Role | Refunds_READ | refunds | — | Yes |
POST /:id/{approve,reject,settle} | 3 methods | refund service | JWT+Role | Refunds_UPDATE | refund rows | 400/404/409 | Yes |
13.2 Request/Response Exhaustiveness
Covered in §8: refund-create without amount (§6.1/8.8), approve-with-method (§6.2), settle-with-reference (§6.3), the receipt disclaimer (§8.4), short-order shape (unavailableQuantity + label), domain errors per endpoint (§8 error tables), the two 409s that are not failures (RETURN_QUANTITY_UNAVAILABLE, REFUND_EXCEEDS_PAID).
13.3 API Diagram Pack
Route ownership (§9.1), sequence per endpoint family (§9.2, backend §7), error decision tree (§9.3), async flow (backend §7.1 — the outbox chain).
13.4 Consumer Integration Notes
| Consumer | Required Knowledge | Failure Handling | Contract Stability |
|---|---|---|---|
| Web frontend | Nine statuses (no returned); derived labels; receipt ≠ tax invoice | 409s per §8; double-tap return = refresh | Stable |
| Mobile app | pollAfterSeconds; COD refund-absence on cancel | ORDER_NOT_CANCELLABLE → offer return | Stable |
| Admin panel | Three permission modules; serials at dispatch; refund settle queue | REFUND_EXCEEDS_PAID → refresh | Stable |
| QA | Quantities-as-truth; short orders; return budget | Reproduce via exact codes | Stable |
| Finance | awaiting_settlement queue; reference required | — | Stable |
13.5 API Tradeoffs and Rationale
| Decision | Chosen Behavior | Alternatives Considered | Why This Tradeoff | Risk | Mitigation |
|---|---|---|---|---|---|
| Background creation | Outbox chain | Synchronous | Paid customer never without a record | Delay | Unique index |
No returned status | History is truth | Statuses | Derived badges | Client assumption | Documented |
| No item status | Quantities add up | Label column | Client work | Label provided | |
| Server computes refund money | No caller amounts | Client amounts | Ceilings enforced | — | CHECKs |
| Human-settled refunds | Money control | Gateway auto | Manual queue | Reference constraint | |
| Three permission modules | Least privilege | One Orders module | Warehouse can't refund | — | Documented |
13.6 API Change Impact
| Change | Affected Consumers | Backend Impact | Data Impact | Migration Needed? | Compatibility Plan |
|---|---|---|---|---|---|
| Fulfillment module later | Dispatch routes | Split services | None | No | Rows shaped for takeover |
| Gateway refund later | Settle flow | New integration | None | No | Human path remains |
14. Zero-Omission API Checklist
- Every controller route is documented (§4, §8, §13.1).
- Every parent route prefix and runtime URL is documented (§2, §4).
- Every DTO field, enum, default, transform and validator is documented (§6, §7).
- Every response field and nullable field is documented (§8).
- Every auth, guard, permission and guest identity branch is documented (§5).
- Every success, validation, not-found, conflict, rate-limit and server-error branch is documented (§8).
- Every DB read/write, queue job and external call is documented (§11, backend §9).
- Every route has examples for minimal request, success response and representative failures (§8).
- Every endpoint family has route, sequence and error diagrams (§9, backend §7).
- Every tradeoff and compatibility risk is documented (§13.5, §13.6).
- The API doc links to backend and features/flows (§1, See Also).
15. Integration Checklist
- Every route from controllers is documented.
- Every DTO field is documented.
- Every enum value is documented.
- Every response envelope is documented.
- Every error code is documented.
- Every auth guard and permission is documented.
- Every cache key, queue job and external call is documented.
- Every diagram matches the current code.
- The API doc links to backend and features/flows.
See Also
- Backend doc: /docs/developer/order/backend
- Features and flows doc: /docs/developer/order/feature
- TDD: not yet published
Order Backend Documentation
Backend architecture, data model, services, and operational behavior for the Order module.
POS Module Overview
The admin-only counter till — draft sales, walk-in customers, pickup or delivery, and completion that takes money, moves stock and creates an ordinary order in one transaction.