vsmsconnect — API — invoices

← all endpoints

GET/api/v1/businesses/:businessId/invoices/batches auth: jwt (administrator | view_invoices | submit_invoices)

Paginated list of import batches for the business. Each batch row carries row counts (imported/failed) and the originating file metadata. Registered before /:invoiceId so the literal 'batches' segment never collides with the dynamic id.

handlers: InvoicesController.listBatches

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID. Enforced by requireBusinessAccess (Administrator bypasses).
offset query
integer
no0Pagination offset. Coerced from string; min 0.
limit query
integer
no50Page size. Coerced from string; min 1, max 200.

Outputs

200
APIResponseDataList<ImportBatchDTO>
Page of import batches with totalRows + hasMore.
GET/api/v1/businesses/:businessId/invoices/daily-fiscal-report auth: jwt (administrator | view_audit | submit_invoices)

Server-side aggregation of fiscalised payments inside a date window. Payment-aware — every fiscalised InvoicePayments row counts as one fiscal event and amounts come from PaymentAmount, so a multi-payment invoice with payments on different days splits correctly. Fires DAILY_FISCAL_REPORT_GENERATED (fire-and-forget) for audit traceability.

handlers: InvoicesController.getDailyFiscalReport

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
dateFrom query
ISO-8601 datetime
yes—Inclusive start of the report window. Normalised to start-of-day server-side. Must be on or before dateTo (Zod refine).
dateTo query
ISO-8601 datetime
yes—Inclusive end of the report window. Normalised to end-of-day (23:59:59.999 UTC) server-side.

Outputs

200
APIResponseDataObject<DailyFiscalReportResponse>
Aggregated payment-level fiscal report for the window.
422
APIError
VALIDATION_ERROR — non-ISO date or dateFrom > dateTo.
GET/api/v1/businesses/:businessId/invoices auth: jwt (administrator | view_invoices | submit_invoices)

Paginated invoice list for the business. RTK infinite-query compatible. Returns invoices in the same shape as the single-invoice GET (with embedded payments[] and line-items omitted).

handlers: InvoicesController.list

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
offset query
integer
no0Pagination offset. min 0.
limit query
integer
no50Page size. min 1, max 200.

Outputs

200
APIResponseDataList<InvoiceDTO>
Page of invoices with totalRows + hasMore.
GET/api/v1/businesses/:businessId/invoices/:invoiceId auth: jwt (administrator | view_invoices | submit_invoices)

Fetch a single invoice with its line items and payments[] embedded. Cross-tenant probing returns 404 since the repo filters by businessId.

handlers: InvoicesController.getById

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Invoice UUID.

Outputs

200
APIResponseDataObject<InvoiceDTO>
Invoice with line items and embedded payments[].
404
APIError
INVOICE_NOT_FOUND — invoice does not exist under this business.
GET/api/v1/businesses/:businessId/invoices/:invoiceId/payments auth: jwt (administrator | view_invoices | submit_invoices)

List every payment under a single invoice. Returns the same payments[] array embedded on GET /invoices/:invoiceId plus per-payment fiscal data (FiscalInvoiceNumber, FiscalTimestamp, etc.). Ordered by PaymentDate ASC, CreatedAt ASC. Lets the FE refresh only the payments list after a per-payment action without re-fetching the parent invoice.

handlers: InvoicesController.listPayments

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Parent invoice UUID. Existence under businessId is verified before payments are listed (prevents cross-tenant probing of payment IDs).

Outputs

200
APIResponseDataObject<GetInvoicePaymentsResponse>
{ payments: InvoicePaymentDTO[] } — possibly empty.
404
APIError
INVOICE_NOT_FOUND — invoice does not exist under this business.
GET/api/v1/businesses/:businessId/invoices/:invoiceId/payments/:paymentId auth: jwt (administrator | view_invoices | submit_invoices)

Direct drill-down for a single InvoicePayments row. Returns the same InvoicePaymentDTO shape that appears inside the parent invoice's payments[] array. Returns 404 when the payment doesn't belong to the (invoiceId, businessId) pair — protects against cross-tenant or cross-invoice probing.

handlers: InvoicesController.getPaymentById

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Parent invoice UUID.
paymentId path
uuid
yes—Payment UUID. Must belong to invoiceId under businessId.

Outputs

200
APIResponseDataObject<GetInvoicePaymentByIdResponse>
Single InvoicePaymentDTO.
404
APIError
INVOICE_NOT_FOUND — payment missing or under a different invoice/business.
POST/api/v1/businesses/:businessId/invoices/reprocess-unmapped auth: jwt (administrator)

Re-evaluates every invoice blocked for MISSING_TAX_MAPPING against the current active TaxRateMappings. For each invoice whose codes are now fully mapped: updates line-item TaxLabel/TaxRatePercent in place, strips MISSING_TAX_MAPPING from FiscalisationBlockReasons, and sets EligibleForFiscalisation = 1. Idempotent — already-eligible invoices are untouched.

handlers: InvoicesController.reprocessUnmapped

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.

Outputs

200
APIResponseDataObject<{ reprocessed: number; nowEligible: number; stillBlocked: number }>
Counts of invoices touched and how many became eligible.
POST/api/v1/businesses/:businessId/invoices/cancel-bulk auth: jwt (administrator | submit_invoices)

Bulk cancel up to MAX_PAYMENTS_PER_FISCALISATION_JOB fiscalised payments in one job. Builds a cancellation row per valid payment, then dispatches every freshly-inserted cancellation in a single FiscalisationJob so the client polls one jobId. Invalid IDs come back in blocked[]; IDs whose original already has a cancellation in flight come back in inFlight[]. Fires INVOICE_PAYMENT_CANCELLATION_REQUESTED audit per dispatched payment.

handlers: InvoicesController.cancelBulk

Inputs

Request body type: CancelInvoicesBulkBody

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
paymentIds body
uuid[]
yes—1..MAX_PAYMENTS_PER_FISCALISATION_JOB payment UUIDs to cancel. Validated by CancelInvoicesBulkBodySchema.

Outputs

202
APIResponseDataObject<BulkCancelInvoiceResponse>
{ jobIds, cancelledPaymentIds, blocked[], inFlight[] } — jobIds is empty when nothing was dispatched.
422
APIError
INVOICE_CANCELLATION_BULK_EMPTY | CERTIFICATE_NOT_CONFIGURED | VALIDATION_ERROR (schema bound exceeded).
POST/api/v1/businesses/:businessId/invoices/:invoiceId/payments/:paymentId/cancel auth: jwt (administrator | submit_invoices)

Cancel a single fiscalised payment. Issues a cancellation row (Sale → Refund / Refund → Sale) with the same line items but BuyerTin set to the seller's TIN and ReferentDocumentNumber pointing back at the original SDC fiscal number, then dispatches it through the standard fiscalisation pipeline. Poll the returned jobId — on success the original payment is marked cancelled by the consumer. Fires INVOICE_PAYMENT_CANCELLATION_REQUESTED audit.

handlers: InvoicesController.cancel

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Parent invoice UUID.
paymentId path
uuid
yes—Payment UUID to cancel. Must be fiscalised and not already in flight.

Outputs

202
APIResponseDataObject<CancelInvoiceResponse>
{ jobId, cancellationPaymentId } — poll the job for terminal status.
409
APIError
INVOICE_CANCELLATION_IN_FLIGHT | INVOICE_NOT_CANCELLABLE | INVOICE_ALREADY_CANCELLED.
422
APIError
INVOICE_TYPE_NOT_SUPPORTED_FOR_CANCELLATION | CERTIFICATE_NOT_CONFIGURED.
POST/api/v1/businesses/:businessId/invoices/:invoiceId/payments/:paymentId/copy auth: jwt (administrator | submit_invoices)

Per-payment Copy. Inserts a new Invoices row with InvoiceType='COPY', CopiesInvoiceId pointing at the source invoice, totals scaled to the source payment's amount, plus a synthetic InvoicePayments row carrying CopiesPaymentId. Source payment's SDC FiscalInvoiceNumber is the V-SDC referent on the new submission; TransactionType is preserved (Copy of Sale → Sale, Copy of Refund → Refund). One-time per source payment (retry of a failed copy allowed). Immediately dispatches for fiscalisation and fires INVOICE_COPY_REQUESTED audit.

handlers: InvoicesController.copy

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Source invoice UUID.
paymentId path
uuid
yes—Source payment UUID. Must be a fiscalised payment under a supported invoice type.

Outputs

202
APIResponseDataObject<CopyInvoiceResponse>
{ invoiceId, invoicePaymentId, invoiceNumber, jobId } — jobId is null when triggerFiscalisation did not queue (e.g. already_fiscalised / all_blocked).
404
APIError
INVOICE_NOT_FOUND.
409
APIError
INVOICE_ALREADY_COPIED.
422
APIError
INVOICE_NOT_COPYABLE | INVOICE_TYPE_NOT_SUPPORTED_FOR_COPY | CERTIFICATE_NOT_CONFIGURED.
POST/api/v1/businesses/:businessId/invoices/:invoiceId/payments/:paymentId/refund auth: jwt (administrator | submit_invoices)

Per-payment Refund. Inserts a new Invoices row with TransactionType='REFUND' and InvoiceType inherited from the source (Normal → Normal Refund, Advance → Advance Refund), RefundsInvoiceId pointing at the source invoice, totals scaled to the source payment's amount, plus a synthetic InvoicePayments row carrying RefundsPaymentId. Source payment's SDC FiscalInvoiceNumber is the V-SDC referent on the new submission. Only valid on a fiscalised SALE payment under a Normal or Advance invoice — refunding a refund is rejected. One-time per source payment (retry of a failed refund allowed). FE button is hidden on Normal/Advance (only surfaced for PROFORMA quotes); backend stays permissive so the service-layer REFUNDABLE_TYPES guard remains the source of truth. Fires INVOICE_REFUND_REQUESTED audit.

handlers: InvoicesController.refund

Inputs

namerequireddefaultdescription
businessId path
uuid
yes—Target business UUID.
invoiceId path
uuid
yes—Source invoice UUID.
paymentId path
uuid
yes—Source SALE payment UUID. Must be fiscalised and not already refunded.

Outputs

202
APIResponseDataObject<RefundPaymentResponse>
{ invoiceId, invoicePaymentId, invoiceNumber, jobId } — jobId null when not queued.
404
APIError
INVOICE_NOT_FOUND.
409
APIError
INVOICE_ALREADY_REFUNDED.
422
APIError
INVOICE_NOT_REFUNDABLE | INVOICE_TYPE_NOT_SUPPORTED_FOR_REFUND | CERTIFICATE_NOT_CONFIGURED.