resellerhq

Invoicing

Bill a customer for sales you have already made, discount an invoice before it is issued, and correct one already sent with a void or a credit note.

ResellerHQ does not take payment from your customers. There is no card form, no payment link and no payout. You collect the money however you already do, by bank transfer, your own card processor or cash, and then mark the invoice paid here. paid is your own record that the money arrived, and nothing checks it.

Raise an invoice

An invoice bills one customer for sales you have already made for them. Each line is a copy of a sale at the price it was made at, so an invoice reads the same for good.

curl -X POST https://resellerhq.com/api/v1/invoices \
  -H "Authorization: Bearer $RHQ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 2f1c9a52-7d0e-4a8e-9a53-0c1f6f3f2b11" \
  -d '{ "customerId": "cus_01K6XJ4Q8ZT3M9R5V2B7WDHNCA", "dueInDays": 14, "taxBps": 2000 }'
  • With customerId alone, the invoice takes every sale of theirs that is not on an invoice yet, oldest first. saleIds bills only the sales you name; GET /sales?invoiced=false lists the ones still waiting.
  • dueInDays is 14 when left out. taxBps adds tax as a share of the subtotal, in basis points, rounded to the nearest minor unit; left out, there is no tax. note is free text.
  • Invoices are numbered INV-0001, INV-0002 and so on across your account. A number is never used twice, and a refused request uses none.
  • An invoice is in one currency. If the customer has sales in two, the request is refused with mixed_currencies; send currency to bill one of them at a time.
  • An invoice holds at most 500 sales. With more waiting, the oldest 500 go on it and the rest go on the next.
ErrorWhy
nothing_to_invoiceThe customer has no sales waiting to be invoiced
mixed_currenciesThe sales are in more than one currency
sale_unavailableA sale you named is not this customer's, or is already on an invoice
invoice_not_openThe invoice is already paid, void or fully credited
invoice_not_voidableThe invoice carries a credit note, so it has to stay standing
invalid_paid_atpaidAt is in the future, or more than a year before the invoice was issued
invalid_discountA discount names a sale that is not on this invoice

A sale is on at most one invoice that still stands. Each sale carries invoiceId, which is null until it is invoiced.

Your billing details

PATCH /account/billing sets how you are identified on invoices, and the defaults a new one falls back to when you do not say otherwise.

curl -X PATCH https://resellerhq.com/api/v1/account/billing \
  -H "Authorization: Bearer $RHQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "legalName": "Northwind Telecom Ltd",
    "addressLines": ["1 Market Street", "San Francisco, CA 94105"],
    "taxId": "12-3456789",
    "billingEmail": "billing@northwind.example",
    "defaultDueDays": 30,
    "defaultTaxBps": 2000,
    "invoiceNote": "Thank you for your business"
  }'
  • Send only what changes; null clears a field. GET /account reads it all back under billing, null for anything you have not set and addressLines: [] for none.
  • defaultDueDays, defaultTaxBps and invoiceNote are used on a new invoice only when that invoice's own dueInDays, taxBps or note is left out; sending one, 0 included, always wins over the default. With neither a default nor a value sent, dueInDays is 14.
  • Every invoice and credit note keeps issuer: a copy of your name and billing details as they stood the moment it was issued. Changing your billing details later never rewrites a document already issued, and issuer is null on one made before you had set any.
  • Live and sandbox are separate accounts, so each keeps billing details of its own.

Discount an invoice

A discount is money off before an invoice is issued: send it in the POST /invoices body, alongside the sales being billed.

curl -X POST https://resellerhq.com/api/v1/invoices \
  -H "Authorization: Bearer $RHQ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9b3a1f2e-5c6d-4e7a-8f9b-1a2b3c4d5e6f" \
  -d '{
    "customerId": "cus_01K6XJ4Q8ZT3M9R5V2B7WDHNCA",
    "discounts": [
      { "saleId": "sal_01K6XJ4Q8ZT3M9R5V2B7WDHNCA", "percentBps": 1000 },
      { "amount": 500, "description": "Loyalty credit" }
    ]
  }'
  • A discount is either a line discount, naming the saleId it comes off, or a whole-invoice discount, applying to the sales' subtotal, when saleId is left out.
  • A discount is either a percentage, percentBps from 1 to 10,000 (1000 is 10%), or a flat amount, amount in the invoice's currency above zero — exactly one of the two.
  • Each discount becomes its own line, after every sale line, with a negative total and no saleId of its own, in the order you sent them. Leave description out and one is written for you: Discount, 10% off northwind.com, Discount, northwind.com, Discount, 10% or plain Discount, depending on whether it is a line or whole-invoice discount and a percentage or an amount.
  • A line discount cannot take that line below zero; discounts together cannot take the invoice's subtotal below zero. Going past either is refused with discount_too_large and a maximum, what was left to discount.
  • Tax is worked out after the discount: it is a share of the subtotal with every line, discounts included, already added up. An invoice holds at most 20 discounts.

Mark it paid

curl -X POST https://resellerhq.com/api/v1/invoices/inv_01K6XJ4Q8ZT3M9R5V2B7WDHNCA/pay \
  -H "Authorization: Bearer $RHQ_API_KEY"

paidAt is when the money arrived, and is now when left out. An invoice is open until you mark it paid, void it, or credit it in full, which closes it as credited instead — see Void or credit below. paid and void are both final: a paid invoice cannot be voided or paid again, and a void one cannot be reopened.

The due date is a whole day. An open invoice has overdue: true from 24 hours after dueAt, when that day has ended everywhere, so an invoice made with dueInDays: 0 is due today and not overdue today. Nothing else happens to an overdue invoice, and no reminder is sent.

curl -X POST https://resellerhq.com/api/v1/invoices/inv_01K6XJ4Q8ZT3M9R5V2B7WDHNCA/share \
  -H "Authorization: Bearer $RHQ_API_KEY"

This is how you hand an invoice to the customer it bills: it answers with { token, createdAt }, and the invoice is then readable at GET /public/invoices/{token} with no API key at all. Pressing the button twice is safe, since a second call returns the same token rather than minting a new one. The public page shows that one invoice and nothing else: no id, no customer id, no sale ids, not even the token itself. DELETE /invoices/{id}/share kills the link at once; sharing again afterwards hands out a different token, and the old one answers 404 for good.

A credit note has a public link of its own, POST /credit-notes/{id}/share and DELETE /credit-notes/{id}/share, with the same meaning, readable at GET /public/credit-notes/{token}.

Void or credit

Both withdraw something about an invoice. Which one is right depends on whether the invoice was ever correct:

  • Void an invoice that should never have existed: it was raised on the wrong sales, for the wrong customer, or by mistake. POST /invoices/{id}/void withdraws it entirely and frees its sales to be billed again, on a new invoice that takes the next number. A void invoice is never paid, never credited, and nothing is owed on it.
  • Credit an invoice that was correctly issued and has already been sent: the work was done and billed right, but some or all of it now needs to come off, for a refund, a cancellation or a goodwill adjustment. A credit note corrects it without touching the original, which the customer may already have a copy of.

Once you have sent an invoice to a customer, voiding it is rarely the right move: they are holding a document that says one thing, and voiding it makes your records say another with no link between them. A credit note names the invoice it corrects, so both documents keep telling the same story.

The two are not interchangeable once either has happened. An invoice that already carries a credit note can no longer be voided: POST /invoices/{id}/void answers invoice_not_voidable, because the credit note names that invoice and has a number of its own in your records, so the invoice has to stay standing. Credit the rest of it instead, which closes it as credited. The same holds the other way: a void invoice takes no credit note (invoice_not_creditable), there being nothing owed on it to correct.

Issue a credit note

curl -X POST https://resellerhq.com/api/v1/invoices/inv_01K6XJ4Q8ZT3M9R5V2B7WDHNCA/credit-notes \
  -H "Authorization: Bearer $RHQ_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7c4d5e6f-1a2b-4c3d-9e8f-0a1b2c3d4e5f" \
  -d '{
    "reason": "Number cancelled after the invoice was sent",
    "lines": [{ "saleId": "sal_01K6XJ4Q8ZT3M9R5V2B7WDHNCA" }]
  }'
  • Credit either lines, naming the invoice lines to credit (leave a line's amount out to credit everything still left on it, or give one to credit only part), or amount, a flat sum off the invoice's subtotal with no lines named — never both.
  • reason is required, and kept with the credit note for good.
  • GET /invoices/{id}/creditable shows what is still left to credit: each line with its credited so far and what remains creditable, and the invoice's own subtotal, tax and total still uncredited.
  • A credit note has its own number, CN-0001 and up, in its own gapless sequence across your account, never shared with invoice numbers.
  • Its tax carries the same proportion as the lines it credits: crediting a line that is 40% of the invoice's subtotal credits 40% of its tax, whatever rate the invoice was taxed at. Crediting an invoice down to nothing always comes out to its exact total, to the penny, whatever the rounding on the way.
  • A credit note can never exceed what is still left uncredited on the invoice, on a line or across it: asking for more answers credit_too_large with a creditable figure, what was left before tax.
  • A credit note is never edited and never deleted. To correct a mistaken credit note, issue an invoice again for the amount, or a second credit note if there is more to take off.
  • A void invoice, or one already credited in full, cannot take another credit note: invoice_not_creditable.
curl https://resellerhq.com/api/v1/credit-notes/crn_01K6XJ4Q8ZT3M9R5V2B7WDHNCA \
  -H "Authorization: Bearer $RHQ_API_KEY"

GET /invoices/{id}/credit-notes lists the credit notes against one invoice, newest first. GET /credit-notes lists every credit note on the account, newest first, narrowed with customerId, invoiceId or q, which looks in the credit note's number and the customer's name and email.

What a credit note does to the invoice

  • Crediting part of an open invoice lowers what is still owed on it: the invoice stays open, and amountDue falls by the credit's total.
  • Crediting an open invoice in full closes it as credited, not paid — nothing more is owed on it, and it was never marked paid. creditedAt records when that happened.
  • Crediting a paid invoice, in part or in full, stays paid: the money already arrived, so there is nothing left to collect. What the credit comes to is instead money owed back to the customer, which shows up on their balance.

Every invoice carries credited, what its credit notes come to so far, tax included, 0 when it has none, and amountDue, what is still owed: total less credited on an open invoice, and 0 on one that is paid, void or fully credited.

What a customer owes

GET /customers/{id}/balance answers with one row for each currency the customer has been sold in:

FieldWhat it holds
uninvoicedSold but not on an invoice yet. Before tax, since tax is set on the invoice. Under zero when the customer is owed a credit
openOn invoices waiting to be paid, overdue ones included, net of any credit notes against them
overdueThe part of open whose due date has passed
paidOn invoices you marked paid, net of any credit notes against them
creditedWhat credit notes have taken off invoices that are not void, credited ones included

Four webhook events follow an invoice: invoice.created, invoice.paid, invoice.voided and credit_note.issued. The first three carry the invoice's id, number, customerId, currency, total and status; credit_note.issued carries the credit note's own id, number, invoiceId, invoiceNumber, customerId, currency, subtotal, tax, total and reason.

Not built yet

  • Paying out what a credit note leaves owed to a customer. The amount is recorded on their balance; settling it is a separate issue.
  • Sending an invoice or a credit note to the customer, reminders, scheduled invoicing, and editing an open invoice.
  • Collecting payment from your customers. An invoice is a record; you take the money.