Correcting, Refunding, and Voiding Invoices

Which API calls to make, in which order, to fix a charge, refund a payment, or void a customer invoice in each invoice status.

The Hint app combines several steps into one workflow when a provider voids an invoice or removes a charge. The public API exposes those steps individually, and each one is only allowed in certain invoice statuses. This guide lists the rules and the recommended sequence for each situation. It applies to both partner integrations and practice developers, since all of these endpoints are practice-scoped (/api/provider/*).

Two rules drive everything below:

  • Charges can only be added, deleted, or repriced while the invoice is in draft. Once an invoice is issued, its amounts are frozen.
  • An invoice can never be overpaid. Hint rejects any payment above the amount due, and will not let you lower the total of an invoice that already has money on it. To correct a paid invoice you must refund first.

Invoice statuses

stateDiagram-v2
    direction LR
    [*] --> draft: Create invoice
    draft --> issued: Issue
    issued --> sent: Email sent
    issued --> paid: Payment covers due
    sent --> paid: Payment covers due
    paid --> issued: Refund
    issued --> draft: Return to draft
    sent --> draft: Return to draft
    issued --> cancelled: Void
    sent --> cancelled: Void
    cancelled --> draft: Return to draft
    draft --> [*]: Delete
statusMeaningAllowed API actions
draftEditable, no financial impact yetAdd, update, delete charges; update invoice; issue; delete
issued, sentBilled to the patient, balance outstandingCreate payment; return to draft; void; email
paidFully paidNone until the practice refunds it in the Hint app, which returns it to issued or sent
cancelledVoidedReturn to draft; delete (both only if the invoice never had a payment)
retrying, bad_debt, disputedPayment collection states managed in the Hint appVoid (retrying, bad_debt only)

cancelled is the API name for a voided invoice. There is no separate "refunded" status: a refund on a paid invoice moves it back to issued (or sent) with a positive due_in_cents, and a void moves it to cancelled.

Read the current status, due_in_cents, and paid_in_cents with Show Customer Invoice before choosing a path.

Draft invoice

Nothing has been billed yet, so correct it directly:

A draft cannot be voided. Calling Void Customer Invoice on a draft returns 422 Invoice can not be cancelled. Use delete instead.

Issued or sent invoice, no payment yet

To fix a line item, reopen the invoice first:

  1. Return Customer Invoice To Draft. This removes the invoice's revenue from reports until you reissue it.
  2. Delete or update the charge as in the draft case above.
  3. Issue Customer Invoice again. The invoice keeps the same id.

Skipping step 1 does not fail loudly. Adding or deleting a charge on an issued invoice returns 422 Invoice must be in draft status, but updating a charge returns 200 and silently drops price_in_cents, quantity, name, and charge_item. Nothing about the invoice's amounts changes. Only description, taxable, and the reporting fields (patient, practitioner, category, location, date) are applied, and the reporting fields only while the invoice's reporting period is unlocked. Compare the returned charge with what you sent if you expected the amount to change.

Two more cases where price_in_cents is dropped even on a draft invoice: a charge that already carries a coverage discount, and a charge for a package or prepaid item. To reprice those, delete the charge and create it again. Likewise, Create Charge never adds a line to an issued invoice: Hint routes the new charge to the payer's most recent draft invoice, or opens a new draft.

To cancel the whole invoice, call Void Customer Invoice:

POST /api/provider/customer_invoices/inv-abc123/void
{
  "void_reason": "Appointment cancelled"
}

void_reason is optional. A void with no payments can be undone later with return to draft, or the voided invoice can be removed with Delete Customer Invoice. Deleted invoices are soft-deleted: they drop out of List All Customer Invoices but remain readable by id.

Return to draft is rejected (422 Invoice can not be returned to draft) when:

  • the invoice has any payment, even a refunded one;
  • a patient credit is applied to it;
  • it belongs to a locked reporting period;
  • it was generated by membership billing.

In those cases void the invoice and create a new one instead.

Paid or partially paid invoice

Hint will not reduce the total of an invoice that has money applied to it, and the public API has no refund call: refunds are issued by the practice in the Hint app. The supported sequence is a refund in the app, a void through the API, then a replacement invoice if the patient still owes something.

Step 1: refund the payments in the Hint app

Open the invoice in Hint and refund each payment in full. Payments processed through Hint (card or bank) go back to the original payment method. Payments recorded with external: true (cash, check, mark as paid) are refunded on the books only, and the practice returns the money outside Hint.

Once the refunds post, the invoice's paid_in_cents drops to 0 and its status moves from paid back to issued (or sent). Confirm that with Show Customer Invoice, or wait for the customer_invoice.updated webhook, before continuing.

Step 2: void through the API

POST /api/provider/customer_invoices/inv-abc123/void
{
  "void_reason": "Visit cancelled, rebilling corrected charges"
}

Voiding an invoice that still has money on it returns 422 Invoice can not be cancelled. The same error is returned when a patient credit is applied to the invoice; credits are applied automatically at issue, and the public API has no call to remove one, so that invoice must be handled in the Hint app.

⚠️

A void that follows a refund is final. The invoice cannot be returned to draft or deleted afterward, because the payment and refund records must stay attached to it.

Step 3: bill the corrected charges

Create a replacement invoice with the lines that should have been billed:

  1. Create Customer Invoice for the same owner.
  2. Create CustomerInvoiceCharge for each corrected line, or post them through Create Charge and let Hint route them.
  3. Issue Customer Invoice.
  4. Create Payment with process: true to charge the saved payment method, or external: true to record a payment collected elsewhere.

If the patient should simply receive money back with no rebilling, stop after step 2.

Alternative: issue a credit instead of a refund

If the patient overpaid by a small amount and will keep buying from the practice, you can leave the paid invoice alone and create a patient credit for the difference with Create Credit. Hint applies available credit automatically the next time one of the patient's invoices is issued. The original charge stays as billed, so use this only when the invoice itself does not need to change.

Quick reference

SituationSequence
Draft, remove one lineDelete charge
Draft, remove everythingDelete invoice
Issued, unpaid, fix a lineReturn to draft, then delete or update charge, then issue
Issued, unpaid, cancelVoid
Paid, cancel and refundRefund in the Hint app, then void
Paid, fix a lineRefund in the Hint app, void, then create, charge, issue, and pay a new invoice
Paid, give back a small differenceCreate credit, or refund in the Hint app
Undo a voidReturn to draft (only if the invoice never had a payment)
Remove a voided invoice entirelyDelete (only if the invoice never had a payment)

None of these actions are idempotent. Voiding an already voided invoice, or issuing an already issued one, returns a 422 or 400 rather than succeeding again. When retrying after a timeout, read the invoice's status first and only resend the call if the transition has not happened.

Payments and overpayment

Create Payment rejects an amount_in_cents greater than the invoice's due_in_cents, and rejects payments against an invoice that already has a pending payment. Omit amount_in_cents to pay exactly the amount due. Payments can only be created against issued, sent, retrying, or bad_debt invoices; a draft, paid, or voided invoice returns 422 Invoice is not payable.

List All Payments shows each payment's gross amount. For processed card payments this can exceed the invoice total when the practice passes processing fees through to the patient (for example amount_in_cents: 1058 on a 1000 invoice); the invoice's paid_in_cents reflects the amount applied to the invoice, not the fee. Refunds are not exposed as separate objects in the public API. Detect a refund from the invoice instead: after a full refund the invoice's paid_in_cents drops to 0 and its status leaves paid.

Quantities: coverage allowances, package items, and inventory

Charges can consume three kinds of quantity: a quantity-limited coverage allowance (for example 1 of 4 covered visits), an item from a prepaid package the patient bought, and stock of a charge item that tracks inventory. Whether removing the charge gives the quantity back depends on how you remove it, because Hint treats the paths as different statements about the service:

  • Draft paths (deleting the charge, deleting a draft invoice, or returning an issued invoice to draft) mean the service was never delivered. Quantities come back.
  • Voiding an issued invoice means the service was delivered but the practice is forgiving the balance. Quantities stay consumed.
QuantityConsumed whenReturned byNot returned by
Coverage allowanceCharge is created on the invoiceDeleting the charge, or deleting the invoice while draftVoid; return to draft on its own (delete the charge after returning to draft)
Prepaid package itemReserved when the charge is created, consumed when the invoice is issuedReturn to draft (back to reserved), deleting the charge, deleting the invoiceVoid
Charge item inventoryInvoice is issuedReturn to draft, deleting the invoiceVoid; deleting a draft has nothing to return because stock is only deducted at issue

Deleting a voided invoice (possible only when it never had a payment) counts as a draft path: the invoice is being erased, not forgiven, so its quantities come back.

So if a visit was cancelled and the charge is on an unpaid invoice, use return to draft and delete the charge rather than void. If the invoice is already paid, the void-and-rebill sequence above will not restore any of these quantities; the practice must adjust them manually in the Hint app.

Webhooks to watch

There are no charge, payment, or refund webhook events. Track invoice corrections through the customer_invoice events described in Webhooks:

EventFires when
customer_invoice.updatedAny attribute changes, including amounts after a charge edit or a refund
customer_invoice.draftReturn to draft
customer_invoice.issuedIssue, or a refund drops a paid invoice back to issued (an invoice that had been emailed returns to sent, which has no event of its own; watch customer_invoice.updated)
customer_invoice.paidPayment covers the full amount due
customer_invoice.cancelledVoid
customer_invoice.destroyedDelete

The payload is the same object returned by Show Customer Invoice. Pass expand[]=charges on a follow-up request if you need the line items.

Error reference

HTTPMessageCause
422Invoice must be in draft statusAdding or deleting a charge on a non-draft invoice
422Invoice can not be cancelledVoiding a draft or disputed invoice, an invoice that still has money on it, or an invoice with a patient credit applied
422Invoice can not be returned to draftInvoice has payments, applied credits, a locked reporting period, or is a draft already
400Invoice is not in draft.Issuing an invoice that is not a draft
400This customer invoice cannot be deleted because it still has financial impact.Deleting an issued or paid invoice, or a voided invoice that has payments
422Invoice is not payableCreating a payment on a draft, paid, or voided invoice
422Invoice has a pending payment or Invoice has an unattempted paymentCreating a payment while an earlier payment is still in flight
422Amount is greater than the amount due on the invoicePayment amount exceeds due_in_cents

Did this page help you?