NoHoldPay

Invoices

Send invoices with line items and tax. Customer pays in crypto. You keep your accounting record.

An invoice is a billed document with line items, optional tax row, and optional billing addresses. The customer receives an email with a Pay button that opens the public viewer at /inv/{UUID}. Clicking Pay there creates a payment tagged source_type='invoice' in your webhooks and lands the customer on the standard checkout.

List page

Sidebar: Invoices. Most-recent 100 per mode.

  • Search by invoice number, customer name, email, or linked payment.
  • Filter with the Status dropdown: All, Draft, Sent, Viewed, Overdue, Paid, Cancelled, Refunded.
  • Each row shows the invoice number (monospace), status badge, customer name and email, the due date, the created date, a status-relative timestamp (sent, opened, or paid), total amount, line-item count, and a Payment linked chip when a payment has been started for the invoice.
  • Top-right CTA: New invoice.

Bulk actions

Tick row checkboxes to reveal Cancel selected (destructive). The action fires immediately. Invoices not in a cancellable status are skipped silently and the result banner reports a count like 4 of 5 cancelled.

Create a draft

Go to Invoices in the sidebar and click New invoice.
Fill in Bill to: customer name (optional) and email (required).

Add line items: description, quantity, unit price, optional tax %. Use Add line to add more.

Pick the currency, set issue and due dates if needed, and customise the Tax label (e.g. VAT, GST). The default label is Tax.

(Optional) Adjust Bill from - pre-filled from your Settings profile, but every field is editable per invoice.

(Optional) Add internal notes and customer-facing terms.

Pick which chains and coins this invoice accepts. Defaults to every coin on every wallet you have.

Click Create draft. The invoice number auto-generates as INV-YYYYMMDD-NNNN unless you provide one.

Drafts are private. Nothing is sent to the customer until you click Send invoice from the detail page.

Bill from / Bill to

Bill from is pre-filled once on first form mount from your Settings profile (business name, logo, address, phone, tax ID). Every field is editable per invoice and the value is snapshotted into the invoice at save time. Editing your Settings later does not retroactively change invoices you have already created or sent.

Bill to holds the customer's name, email (required), and an optional billing address with phone and tax ID. Both phone and tax ID appear on the printed and customer-facing invoice when set.

Line items and tax

Each line has a description, quantity, unit price, and an optional tax percent (enter 10 for 10%).

The Tax label field above the table lets you rename the tax row to anything up to 50 characters: VAT, GST, IVA, etc. The default is Tax. The label appears on the table column header, the totals row, the public viewer, and the printed invoice. Customer emails carry only the total and a link to the invoice, so the label does not appear there.

Totals are computed server-side in decimal at save time. The form preview is just that - a preview.

Send and resend

From the detail page:

  • Send invoice - flips a draft to Sent and emails the customer. Subject: Invoice <number> from <your business name> - <total> <currency> due.
  • Resend - re-sends the email when status is Sent, Viewed, or Overdue. Subject: Reminder: Invoice <number> from <your business name> - <total> <currency> due. Fires the invoice.reminder webhook.

The from <your business name> segment is dropped from both subjects when no business name is set.

Both endpoints are rate-limited to keep merchant emails from looking like spam.

Detail page

Top-right action buttons (conditional):

ButtonWhen
Send invoiceDraft only
EditDraft only
PreviewDraft only (opens the customer-facing view with a disabled Pay button)
ResendStatus is Sent, Viewed, or Overdue
Copy public linkAny status except Draft
Print / PDFAlways (opens the print view - use your browser's Print dialog to save as PDF)
CancelStatus is Sent or Viewed
Delete draftDraft only, when no payment has spawned

The page layout (sections stack full-width - there is no sidebar):

  1. Three summary tiles: Total, Due, Status.
  2. Line items table with subtotal, tax row, and total.
  3. Two cards side by side: Bill to (customer name, email, billing address) and Payment (the public invoice link with a copy button, shown for every status, plus the linked payment ID that opens the payment detail page - or Not paid yet - and the invoice currency). An open-in-new-tab icon next to the link opens the public invoice.
  4. Timeline card (Created, Sent, Viewed, Paid, Cancelled - when each happened).
  5. Accepted payment methods card.
  6. Terms and Internal notes cards, shown only when set.

Lifecycle

Draft -> Sent -> Viewed -> Paid -> Refunded
                    |
                    +-> Cancelled
StatusMeaning
DraftCreated but not sent. Editable. Deletable.
SentEmailed to the customer. Share URL is live.
ViewedCustomer opened the public URL at least once.
PaidA spawned payment confirmed.
OverdueDerived view: Sent past its due date without payment. An invoice the customer has already opened stays Viewed and does not flip to Overdue. Stored status is unchanged.
CancelledYou cancelled (Sent or Viewed only). Public URL stops accepting payments.
RefundedA previously paid invoice was refunded.

Cancel and delete

ActionAllowed when
EditStatus is Draft
CancelStatus is Sent or Viewed
Delete draftStatus is Draft and no payment has spawned

Cancelling closes the share URL immediately. The invoice record stays in the dashboard. In-flight payments already spawned from the invoice keep working.

Public viewer (/inv/{UUID})

The customer's share URL. Drafts and soft-deleted invoices return 404.

  • Your logo (or an initial avatar) and business name lead the card with a "sent you this invoice" subline, and a status pill sits alongside: Open (for Sent or Viewed), Overdue, Paid, Cancelled, Refunded.
  • The amount due is the headline. The invoice number and issue date sit in a meta strip below it.
  • Line items render as stacked description and line-total rows. A qty x unit price subline appears only when the quantity is above 1. Tax appears as its own labeled row in the totals.
  • From and Billed to blocks render below the totals when you filled them in.
  • For payable invoices, a large pay button that reads Pay <amount> with crypto. Clicking it spawns a payment and hard-navigates the customer to the standard checkout.
  • For paid, cancelled, or refunded invoices, a banner replaces the Pay button with the appropriate message.
  • Footer: Powered by NoHoldPay.

The first time a customer opens the public URL, the platform fires the invoice.viewed webhook. Subsequent visits do not re-fire the event.

Open Print / PDF from the detail page. It opens /invoices/{id}/print?auto=1 in a new tab, which auto-triggers your browser's print dialog. From there, pick Save as PDF or print on paper. There is no separate PDF-download endpoint.

The print view header reads INVOICE with the invoice number in monospace. The page shows a 3-column row for From / Billed to / Dates plus the full line-items table.

Webhooks

EventWhen
invoice.sentYou sent a draft (Send invoice). Resends fire invoice.reminder only.
invoice.viewedThe customer opened the public URL for the first time.
invoice.paidA spawned payment confirmed. Also triggers the customer's "Payment received" email.
invoice.refundedA previously paid invoice was refunded.
invoice.reminderYou triggered Resend.

There is no invoice.cancelled webhook. Cancelling closes the public URL but does not emit a webhook.

Payload shape (some fields vary by event, see below):

{
  "invoice_id": "...",
  "invoice_number": "INV-20260514-0001",
  "status": "paid",
  "amount": "120.00000000",
  "currency": "USD",
  "customer_email": "[email protected]",
  "public_url": "https://checkout.noholdpay.com/inv/...",
  "paid_at": "2026-05-14T12:00:00Z",
  "payment_id": "...",
  "due_date": "2026-05-28"
}

paid_at, payment_id, and due_date are optional and depend on the event. invoice.paid always carries payment_id but omits paid_at and due_date. invoice.refunded carries refunded_at instead of paid_at. On invoice.sent, invoice.viewed, and invoice.reminder each of the three appears only when set. amount serializes at 8 decimal places ("120.00000000"), so do not assert on a 2-decimal string.

See Webhooks for signature verification.

Errors you may see

CodeWhen
INVALID_CUSTOMER_EMAILEmail is empty or invalid.
INVALID_LINE_ITEMSLine items are empty, negative, or non-decimal.
NO_VIABLE_OPTIONSThe chains and coins you allowed don't intersect any wallet you have.
INVOICE_NUMBER_TAKENA custom invoice number collides with an existing one.
INVOICE_NOT_EDITABLEEdit attempted on a non-draft.
INVOICE_NOT_SENDABLESend attempted on a non-draft.
INVOICE_NOT_RESENDABLEResend attempted outside Sent / Viewed / Overdue.
INVOICE_NOT_CANCELLABLECancel attempted outside Sent / Viewed.
INVOICE_NOT_DELETABLEDelete attempted on a non-draft or after a payment spawned.
INVOICE_NOT_PAYABLEA customer tried to pay an invoice that is not accepting payment.
INVOICE_ALREADY_PAIDA customer tried to pay but a non-terminal payment is already linked.

On this page