Cart Module — Admin
HTTP surface for platform-admin oversight of carts: paginated browse + detail view (priced, vendor-allocated), force-discard, manual reservation release, and a suite of analytics…
HTTP surface for platform-admin oversight of carts: paginated browse + detail view (priced, vendor-allocated), force-discard, manual reservation release, and a suite of analytics queries (funnel, abandonment, top-removed variants, coupon usage, gift attach rate, per-customer cohort).
Source:
api-modules/cart/src/controllers/admin-cart.controller.ts,api-modules/cart/src/controllers/admin-cart-analytics.controller.ts.Carts are first-class entities — every storefront mutation transitions a single row through a
pending → checkout_prepared → converted(orabandoned/discarded) lifecycle. The admin surface exposes the same view the storefront sees plus the operational escape hatches needed to unstick reservations.
Conventions
Authentication
All endpoints require a Better-Auth admin session and a role granting the matching cart:* permission.
| Endpoint group | Permission |
|---|---|
GET /admin/carts, GET /admin/carts/:id | cart: view |
DELETE /admin/carts/:id, POST /admin/carts/:id/release-reservations | cart: manage |
GET /admin/cart-analytics/** | cart: view |
Response envelope
Successful responses are wrapped by ResponseInterceptor:
{
"data": <payload>,
"message": "Success",
"statusCode": 200,
"metadata": { /* optional, e.g. pagination */ }
}Error envelope
statusCode | errorCode examples |
|---|---|
| 400 | BAD_REQUEST, VALIDATION_ERROR |
| 401 | UNAUTHORIZED |
| 403 | FORBIDDEN |
| 404 | NOT_FOUND |
| 500 | INTERNAL_SERVER_ERROR, DATABASE_ERROR |
Money fields
All money values are integer subunits (paise / cents). Service-layer pricing is single-currency-per-tenant.
Domain types
CartListItem
type CartStatus = "pending" | "checkout_prepared" | "converted" | "abandoned" | "discarded";
type Platform = "APP" | "WEB" | "BOTH";
type CartListItem = {
cartId: string;
cartToken: string;
customerId: string | null; // null for guest carts
status: CartStatus;
platform: Platform;
lineCount: number;
vendorIds: string[]; // distinct vendor ids present in the cart
lastActivityAt: string; // ISO
createdAt: string; // ISO
};CartResponse
The full vendor-allocated view used by both storefront and admin. Identical shape to the storefront response — admin sees the same priced breakdown the customer does.
type CartResponse = {
cartId: string;
cartToken: string;
customerId: string | null;
status: CartStatus;
platform: Platform;
version: number; // optimistic-lock counter
bags: Array<{
vendorId: string;
vendor: { name: string; slug: string; logo: string | null } | null;
lines: CartLineResponse[];
subtotal: number; // subunits
discountAllocated: number;
totalBeforeShippingAndTax: number;
shipping: { cost: number } | null; // null when no shipping provider resolves
}>;
cartTotals: {
subtotal: number;
discountTotal: number;
shippingTotal: number;
total: number;
};
appliedCoupons: Array<{
code: string;
discountId: string;
individualUse: boolean;
freeShipping: boolean;
allocations: Array<{ vendorId: string; amount: number }>;
}>;
pendingGifts: Array<{
ruleId: string;
slotCount: number;
alreadySelectedVariantIds: string[];
optionVariantIds: string[];
}>;
deliveryAddressId: string | null;
deliveryAddress: AddressResponse | null; // resolved fresh on every read
lastActivityAt: string;
createdAt: string;
};AddressResponse is the same shape returned by /store/addresses — see customer.md.
Browse + admin actions
Base path: /admin/carts.
GET /admin/carts — List carts
Required permission: cart: view. Uses offset-based pagination (not page/limit) and supports first-class filters for status, customer, vendor, and a lower-bound on lastActivityAt.
Query
| Name | Type | Default | Notes |
|---|---|---|---|
status | CartStatus? | — | Filter by lifecycle status |
customerId | string? | — | Filter to one customer |
vendorId | string? | — | Filter to carts containing at least one line from this vendor |
limit | int | 50 | 1..500 |
offset | int | 0 | >= 0 |
activeSince | ISO datetime? | — | Inclusive lower bound on lastActivityAt |
Response 200 — paginated envelope of CartListItem[].
GET /admin/carts/:id — Get cart detail
Required permission: cart: view. Returns the fully priced, vendor-allocated view (same shape as the storefront cart response).
Response 200 — CartResponse.
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Unknown cart id |
DELETE /admin/carts/:id — Force-discard a cart
Required permission: cart: manage. Soft-transitions the cart to discarded. Active inventory reservations held by the cart are released as part of the transition.
Response 204 No Content.
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Unknown cart id |
POST /admin/carts/:id/release-reservations — Release reservations
Required permission: cart: manage. Operational escape hatch — releases every active inventory reservation batch held by the cart without changing cart status. Use when an abandoned-but-not-yet-expired cart is blocking stock that needs to be freed manually.
Response 200
{ "data": { "releasedBatches": 2 }, "message": "Success", "statusCode": 200 }Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Unknown cart id |
Cart analytics
Base path: /admin/cart-analytics. Every endpoint requires cart: view.
The window query is shared across most endpoints:
| Name | Type | Default | Notes |
|---|---|---|---|
from | ISO datetime? | 30 days ago | Inclusive |
to | ISO datetime? | now | Inclusive |
granularity | "hourly" | "daily" | "daily" | Bucket size for the funnel |
All analytics return data scoped to the entire platform (vendorId: null); the parallel vendor-self endpoints under /vendor/cart-analytics scope to the active vendor.
GET /admin/cart-analytics/funnel — Platform funnel rollup
Query — AnalyticsRangeQuery (above).
Response 200
type FunnelBucket = {
bucketAt: string; // ISO; start of the bucket window
cartsCreated: number;
itemsAdded: number;
itemsRemoved: number;
couponsApplied: number;
giftsAttached: number;
checkoutsPrepared: number;
cartsAbandoned: number;
uniqueActiveCarts: number;
};
type FunnelResponse = {
granularity: "hourly" | "daily";
vendorId: string | null; // null at this endpoint
buckets: FunnelBucket[];
totals: Omit<FunnelBucket, "bucketAt">;
};GET /admin/cart-analytics/abandonment — Abandonment summary
Query — AnalyticsRangeQuery.
Response 200
type AbandonmentResponse = {
vendorId: string | null; // null at this endpoint
cartsCreated: number;
cartsAbandoned: number;
checkoutsPrepared: number;
abandonmentRate: number; // cartsAbandoned / cartsCreated, 0..1
checkoutRate: number; // checkoutsPrepared / cartsCreated, 0..1
};GET /admin/cart-analytics/top-abandoned-variants — Most-removed variants
Query
| Name | Type | Default | Notes |
|---|---|---|---|
from, to, granularity | — | — | Shared window |
limit | int | 10 | 1..100 |
Response 200
type TopVariantsResponse = {
vendorId: string | null;
items: Array<{
variantId: string;
vendorId: string | null;
removedCount: number;
}>;
};GET /admin/cart-analytics/coupon-usage — Coupons applied count
Query — AnalyticsRangeQuery.
Response 200 — service-defined payload (couponsApplied total over the window).
GET /admin/cart-analytics/gift-attach-rate — Gift attach rate
Query — AnalyticsRangeQuery.
Response 200 — service-defined payload (giftsAttached / cartsCreated).
GET /admin/cart-analytics/cohort/:customerId — Per-customer event breakdown
Path params
| Name | Notes |
|---|---|
customerId | Better-Auth user id |
Query — AnalyticsRangeQuery.
Response 200
type CohortResponse = {
customerId: string;
byEvent: Array<{ eventType: string; total: number }>;
};Related modules
admin-rbac— gates every endpoint viacart:view/cart:manage. Seeadmin-rbac.md.order— converted carts produce orders via the order placement flow.inventory— owns the reservation batches thatrelease-reservationsflushes.discount— produces theappliedCoupons[]snapshot embedded inCartResponse.free-gift— produces thependingGifts[]selection state.customer— provides theAddressResponseresolved intodeliveryAddress. Seecustomer.md.shipping— supplies the per-bag shipping cost.
Banner Module — Admin
HTTP surface for managing promotional banners attached to catalog entities (categories, brands, tags, ingredients). Banner targets a single entity via a polymorphic (entityType,…
Catalog Module — Admin
HTTP surface for platform-admin reads of products + variants and full CRUD over the platform-wide taxonomy (brands, categories, tags, ingredients), with a vendor-request approval…