Supercommerce API Docs
Admin API

Invoice Module — Admin

HTTP surface for admin invoice access — async generation with SSE readiness notification, per-vendor GST tax invoices, lazily generated and cached; check status, enqueue, stream, download, or force regeneration for any order.

HTTP surface for platform-admin invoice access. Invoices are issued per vendor sub-order (the Amazon model): a multi-vendor order produces one tax invoice per seller, each under that vendor's own GSTIN. Generation is lazy and async: the first POST …/generate call enqueues a background render job; an SSE stream reports when it is ready; repeat downloads stream the cached PDF instantly. Admins can access any order and force a regeneration.

Source: api-modules/invoice/src/controllers/admin-invoices.controller.ts.

Invoices are lazily materialised: the row (with its immutable, per-(vendor, financial-year) serial) and the rendered PDF are created on first generation and cached in object storage. Later downloads stream the cached PDF; a regenerate re-renders against the same serial (a GST serial never changes). The seller identity (legal name, GSTIN, address) is snapshotted at first issue from vendor_profile; templated parts (logo, declaration, customer-care lines, QR, authorised signatory, currency symbol, serial prefix) come from the admin invoice settings group. The logo falls back to store.branding.logo_url when unset.


Conventions

Authentication

All endpoints require a Better-Auth admin session and a role granting the matching permission.

PermissionGrants
invoice:viewStatus checks, enqueue, download, and stream for any order.
invoice:regenerateForce a fresh render against the same serial.

The SSE stream (/stream) is authenticated via the session cookie, which the browser EventSource sends automatically.

Response shape

  • Status / generate endpoints return the standard JSON envelope: { data, message, statusCode }.
  • Download (GET …/invoice) streams raw application/pdf bytes with Content-Disposition: attachment — no JSON envelope.
  • SSE stream (GET …/invoice/stream) returns text/event-stream — no JSON envelope.

Generation flow

POST …/invoice/generate  (invoice:view)

       ├─ status: "ready"  ──►  GET …/invoice  (streams cached PDF)

       └─ status: "pending"  ──►  open GET …/invoice/stream

                                         ├─ { status:"pending", heartbeat:true }  (periodic)
                                         ├─ { status:"ready", downloadUrl }  ──►  GET …/invoice
                                         └─ { status:"failed", error }

On page load, call GET …/invoice/status to render the current state without triggering a render.


Endpoints — Order level

Base path: /admin/orders/:orderId/invoice…

Check order invoice status

GET /admin/orders/:orderId/invoice/status · invoice:view

Returns the merged status across all vendor sub-orders for the given order, without enqueuing anything.

Response (data field):

{
  "status": "ready" | "pending" | "failed" | "none",
  "invoiceNumber": null,          // null at order level (per-vendor)
  "downloadUrl": "…"  | null,     // set only when ready
  "streamUrl":  "…"  | null,      // set only when pending or failed
  "jobId":      "…"  | null
}

status: "none" is returned when the order is still in pending_payment.


Enqueue order invoice generation

POST /admin/orders/:orderId/invoice/generate · invoice:view

Enqueues render jobs for all non-cancelled vendor sub-orders if not already cached. Idempotent.

Always returns HTTP 200. Response (data field):

{
  "status": "ready" | "pending",
  "jobId": "…",
  "downloadUrl": "…" | null,
  "streamUrl":   "…" | null
}

Download order invoice (all vendors merged)

GET /admin/orders/:orderId/invoice · invoice:view

Streams one PDF merging every non-cancelled vendor sub-order's invoice. Returns 409 while still generating. Filename: invoice-{orderNumber}.pdf.


Stream order invoice readiness (SSE)

GET /admin/orders/:orderId/invoice/stream · invoice:view

Opens a Server-Sent Events connection. Each data: frame is JSON:

{ "status": "pending", "heartbeat": true }     // periodic
{ "status": "ready",   "downloadUrl": "…" }    // terminal
{ "status": "failed",  "error": "…" }          // terminal

Closes after ~60 s. Re-check GET …/invoice/status and reconnect if still pending.


Regenerate order invoices

POST /admin/orders/:orderId/invoice/regenerate · invoice:regenerate

Forces a fresh render for every vendor sub-order of the order, against their existing immutable serials. Overwrites the cached PDFs. Returns the same shape as POST …/generate.


Endpoints — Sub-order level

Base path: /admin/orders/:orderId/vendors/:orderVendorId/invoice…

Check sub-order invoice status

GET /admin/orders/:orderId/vendors/:orderVendorId/invoice/status · invoice:view

Returns the status for a single vendor sub-order without enqueuing anything.

Response (data field):

{
  "status": "ready" | "pending" | "failed" | "none",
  "invoiceNumber": "INV-2526-000001" | null,
  "downloadUrl": "…"  | null,
  "streamUrl":  "…"  | null,
  "jobId":      "…"  | null
}

Enqueue sub-order invoice generation

POST /admin/orders/:orderId/vendors/:orderVendorId/invoice/generate · invoice:view

Enqueues the render job for the vendor sub-order if not already cached. Idempotent.

Always returns HTTP 200. Response (data field):

{
  "status": "ready" | "pending",
  "jobId": "…",
  "downloadUrl": "…" | null,
  "streamUrl":   "…" | null
}

Download a single vendor's invoice

GET /admin/orders/:orderId/vendors/:orderVendorId/invoice · invoice:view

Streams the cached application/pdf when ready. Returns 409 while still generating. Filename: invoice-{serial}.pdf (e.g. invoice-INV-2526-000001.pdf).


Stream sub-order invoice readiness (SSE)

GET /admin/orders/:orderId/vendors/:orderVendorId/invoice/stream · invoice:view

Opens a Server-Sent Events connection. Same frame shape as the order-level stream:

{ "status": "pending", "heartbeat": true }
{ "status": "ready",   "downloadUrl": "…" }
{ "status": "failed",  "error": "…" }

Closes after ~60 s. Re-check status and reconnect if needed.


Regenerate a single vendor's invoice

POST /admin/orders/:orderId/vendors/:orderVendorId/invoice/regenerate · invoice:regenerate

Forces a fresh render for the vendor sub-order against its existing immutable serial. Overwrites the cached PDF. Returns the same shape as POST …/generate.


Configuration

Invoice presentation and numbering live under the admin settings group invoice (see the Settings docs): title, logo_url, number_prefix (≤ 4 chars; serial is {prefix}/{FY}/{000001}), currency_symbol, declaration_text, customer_care_lines, show_authorized_signatory, authorized_signatory_label, authorized_signatory_image_url, show_qr, qr_image_url, footer_note.

On this page