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
status | Meaning | Allowed API actions |
|---|---|---|
draft | Editable, no financial impact yet | Add, update, delete charges; update invoice; issue; delete |
issued, sent | Billed to the patient, balance outstanding | Create payment; return to draft; void; email |
paid | Fully paid | None until the practice refunds it in the Hint app, which returns it to issued or sent |
cancelled | Voided | Return to draft; delete (both only if the invoice never had a payment) |
retrying, bad_debt, disputed | Payment collection states managed in the Hint app | Void (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:
- Remove a line with Delete CustomerInvoiceCharge.
- Change price, quantity, or name with Update CustomerInvoiceCharge.
- Remove the whole invoice with Delete Customer Invoice. This also deletes its charges.
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:
- Return Customer Invoice To Draft. This removes the invoice's revenue from reports until you reissue it.
- Delete or update the charge as in the draft case above.
- 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:
- Create Customer Invoice for the same
owner. - Create CustomerInvoiceCharge for each corrected line, or post them through Create Charge and let Hint route them.
- Issue Customer Invoice.
- Create Payment with
process: trueto charge the saved payment method, orexternal: trueto 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
| Situation | Sequence |
|---|---|
| Draft, remove one line | Delete charge |
| Draft, remove everything | Delete invoice |
| Issued, unpaid, fix a line | Return to draft, then delete or update charge, then issue |
| Issued, unpaid, cancel | Void |
| Paid, cancel and refund | Refund in the Hint app, then void |
| Paid, fix a line | Refund in the Hint app, void, then create, charge, issue, and pay a new invoice |
| Paid, give back a small difference | Create credit, or refund in the Hint app |
| Undo a void | Return to draft (only if the invoice never had a payment) |
| Remove a voided invoice entirely | Delete (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.
| Quantity | Consumed when | Returned by | Not returned by |
|---|---|---|---|
| Coverage allowance | Charge is created on the invoice | Deleting the charge, or deleting the invoice while draft | Void; return to draft on its own (delete the charge after returning to draft) |
| Prepaid package item | Reserved when the charge is created, consumed when the invoice is issued | Return to draft (back to reserved), deleting the charge, deleting the invoice | Void |
| Charge item inventory | Invoice is issued | Return to draft, deleting the invoice | Void; 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:
| Event | Fires when |
|---|---|
customer_invoice.updated | Any attribute changes, including amounts after a charge edit or a refund |
customer_invoice.draft | Return to draft |
customer_invoice.issued | Issue, 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.paid | Payment covers the full amount due |
customer_invoice.cancelled | Void |
customer_invoice.destroyed | Delete |
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
| HTTP | Message | Cause |
|---|---|---|
| 422 | Invoice must be in draft status | Adding or deleting a charge on a non-draft invoice |
| 422 | Invoice can not be cancelled | Voiding a draft or disputed invoice, an invoice that still has money on it, or an invoice with a patient credit applied |
| 422 | Invoice can not be returned to draft | Invoice has payments, applied credits, a locked reporting period, or is a draft already |
| 400 | Invoice is not in draft. | Issuing an invoice that is not a draft |
| 400 | This customer invoice cannot be deleted because it still has financial impact. | Deleting an issued or paid invoice, or a voided invoice that has payments |
| 422 | Invoice is not payable | Creating a payment on a draft, paid, or voided invoice |
| 422 | Invoice has a pending payment or Invoice has an unattempted payment | Creating a payment while an earlier payment is still in flight |
| 422 | Amount is greater than the amount due on the invoice | Payment amount exceeds due_in_cents |
Updated 1 day ago

