Bulk Invoices — Vendor
HTTP surface for a vendor to batch-generate invoices for many of their own sub-orders at once, merged into a single downloadable PDF, with async status polling and retry of failed orders.
HTTP surface for a vendor to select many of their own sub-orders and request one merged invoice PDF. A BullMQ worker generates (or reuses the cached) invoice for each selected sub-order, then concatenates every successfully-generated PDF into a single file. A batch finishes ready as soon as at least one sub-order succeeded — it only finishes failed when every one did. Failed sub-orders can be retried without re-selecting the whole batch.
Source:
api-modules/invoice/src/controllers/vendor-bulk-invoices.controller.ts.Scoped to the active vendor (
resolveActiveVendorId). A batch id belonging to another vendor returns 404. A requested sub-order that belongs to another vendor is recorded as a failed line item in the batch, not a blanket 404 for the whole request.
Conventions
Authentication
All endpoints require a Better-Auth vendor session. Every read/write is scoped to the caller's vendor id.
Response shape
- Create / retry / status endpoints return the standard JSON envelope:
{ data, message, statusCode }. - List returns the standard paginated envelope:
{ data, message, statusCode, metadata: { total, limit, offset, hasMore } }. - Download (
GET …/:id/download) streams rawapplication/pdfbytes withContent-Disposition: attachment— no JSON envelope.
Generation flow
POST /vendor/bulk-invoices { orderVendorIds: [...] }
│
▼
bulk_invoice_job created (status: pending) — one item per requested order
│
▼
background worker generates each order's invoice, then merges the
successful PDFs into one file
│
├─ at least one order succeeded ──► status: "ready" (fileName/fileSize set)
└─ every order failed ──► status: "failed"
GET /vendor/bulk-invoices — poll the batch list (or a single id) for status
GET /vendor/bulk-invoices/:id/download — download the merged PDF once ready
POST /vendor/bulk-invoices/:id/retry — re-run only the failed orders in the backgroundEndpoints
Start a batch
POST /vendor/bulk-invoices
Request body:
{ "orderVendorIds": ["…", "…"] } // 1–200 sub-order idsAn id that doesn't belong to the caller's vendor (but does exist) is recorded as a failed line item, not rejected outright. An id that doesn't exist at all fails the whole request with 400 (the vendor-admin UI only ever sends ids off the caller's own orders list, so this indicates a malformed request).
Response (data field) — a batch row:
{
"id": "…",
"status": "pending" | "processing" | "ready" | "failed",
"orderCount": 20,
"successCount": 0,
"failedCount": 0,
"fileName": null,
"fileSize": null,
"retryCount": 0,
"error": null,
"createdAt": "…",
"startedAt": null,
"completedAt": null,
"downloadUrl": null // set only when status = ready
}List my batches
GET /vendor/bulk-invoices
Standard offset/limit pagination (querySchema) plus an optional status filter. Rows are the same shape as the create response, newest first.
Batch status + per-order breakdown
GET /vendor/bulk-invoices/:id
Same row shape as above, plus an items array — one entry per requested sub-order:
{
"...": "batch row fields",
"items": [
{
"orderVendorId": "…",
"orderNumber": "ORD-1042" | null,
"status": "pending" | "success" | "failed",
"error": "…" | null
}
]
}Retry failed orders
POST /vendor/bulk-invoices/:id/retry
Allowed only when the batch is ready or failed and has at least one failed item — returns 409 otherwise (including while a batch is still processing). Resets every failed item back to pending and re-runs them in the background; the merged PDF is rebuilt from the full current set of successful invoices (previously-succeeded ones are not re-rendered), so a partially-fixed retry still produces one complete file.
Response: the updated batch row (status: "processing", retryCount incremented).
Download the merged PDF
GET /vendor/bulk-invoices/:id/download
Streams the merged application/pdf once the batch is ready. Returns 404 otherwise (not yet ready, or the batch failed outright).
Filename: bulk-invoices-{id}.pdf (or the batch's stored fileName).
Analytics Module — Vendor surface
Vendor-facing HTTP surface for precomputed dashboard snapshots (sales, earnings, fulfilment, inventory KPIs for today / 7d / 30d / all-time) and a live SSE paid-order feed.
Cart Module — Vendor surface
Vendor-facing HTTP endpoints for browsing carts that contain the vendor's own products and for vendor-scoped cart funnel analytics. There are no write operations: a vendor cannot…