Invoices overview

Itemized statements of what a customer owes.

View as MarkdownInstall skills

An invoice is an itemized statement VINR generates from a subscription cycle or that you create directly, then collects payment against. It is the bridge between Billing (deciding what a customer owes) and Payments (actually collecting it).

The invoice lifecycleAsk

Every invoice — whether subscription-generated or hand-built — moves through the same states. Knowing the current status tells you exactly what you can and cannot change.

StatusMeaningMutable?
draftBeing assembled. Line items can be added or removed.Yes
openFinalized and awaiting payment. Amounts are locked.No (totals)
paidCollected in full.No
uncollectibleWritten off after dunning gave up.No
voidCancelled before payment; reverses any accounting.No

Finalizing is the one-way door. A draft becomes open once you finalize it (or VINR auto-finalizes a subscription invoice an hour after it is created). After that, the only ways out are payment, void, or uncollectible.

How collection worksAsk

When an invoice finalizes, VINR computes the amount due, then attempts a payment against the customer's default method based on the invoice's collection_method:

  • charge_automatically — VINR charges the saved method immediately and runs dunning on failure. This is the default for subscriptions.
  • send_invoice — VINR issues a hosted invoice page and payment link; the customer pays on their own time, bounded by days_until_due.

Discounts from coupons and tax are applied during finalization, so the total you see on an open invoice already reflects them.

Create an invoice directlyAsk

For one-off charges that do not belong to a subscription — a setup fee, professional services, a manual top-up — build the invoice yourself. Add invoice items first, then finalize.

import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

// 1. Attach line items to the customer's pending (draft) invoice.
await vinr.invoiceItems.create({
  customer: 'cust_8aJ2k',
  amount: 15000,                 // €150.00, integer minor units
  currency: 'EUR',
  description: 'Onboarding setup fee',
});

// 2. Create the invoice that sweeps up pending items.
const invoice = await vinr.invoices.create({
  customer: 'cust_8aJ2k',
  collectionMethod: 'send_invoice',
  daysUntilDue: 14,
});                               // status: "draft", id: "inv_..."

// 3. Finalize to lock totals and open it for payment.
const open = await vinr.invoices.finalize(invoice.id);
console.log(open.status, open.total, open.hostedInvoiceUrl);
// "open" 15000 "https://pay.vinr.com/inv_..."

The same flow over raw REST hits POST https://api.vinr.com/v1/invoices with an X-Api-Key: <key> header; use the sandbox.api.vinr.com base and a test card such as 4242 4242 4242 4242 to walk it end to end.

Reading and reconcilingAsk

Each finalized invoice carries the numbers finance teams reconcile against. The key fields:

Prop

Type

Every invoice exposes a stable hostedInvoiceUrl (a branded payment page) and an invoicePdf link, so you rarely need to render statements yourself.

Resulting eventsAsk

Drive your accounting and fulfilment off webhooks rather than polling. The lifecycle emits:

  • invoice.finalized — totals are locked; safe to record as a receivable.
  • invoice.paid — collected in full; grant access or ship the goods here.
  • invoice.payment_failed — collection failed; dunning takes over.
  • invoice.voided — reversed before payment; back out any receivable.
const event = vinr.webhooks.verify(payload, req.headers['x-vinr-signature']);

if (event.type === 'invoice.paid') {
  const invoice = event.data.object;        // "inv_..."
  await fulfilAccess(invoice.customer, invoice.lines);
}

Edge casesAsk

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page