Data behind the hosted invoice page
const url = 'http://localhost:8080/v1/public/invoices/example';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url http://localhost:8080/v1/public/invoices/exampleUnauthenticated. The invoice’s public token in the URL is the only credential. Paid and voided invoices stay viewable. A draft is never shown: it returns 404 like an unknown token. Share the page link with customers instead of calling this route.
Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Responses
Section titled “Responses”The invoice as the page shows it.
What the hosted invoice page shows. It leaves out internal ids and
Stripe details. bill_to and the company details in branding
come from the same place as the invoice PDF’s.
object
object
Lifecycle state of the invoice. uncollectible marks an invoice
a dunning policy has given up collecting (no further retries) while
keeping it on the books — distinct from voided (cancelled, owes
nothing).
Payment-attempt status. unknown marks a Stripe charge attempt that
returned an ambiguous error (5xx, timeout, connection reset) where
we cannot tell whether Stripe processed the charge. With a payment
intent recorded, Hardcount asks Stripe for it after a short wait. With
none (a parked invoice), Hardcount lists the customer’s payments at
Stripe every billing tick: it records a payment for the invoice that
appears, and if none has appeared 24 hours after the attempt the
invoice goes back to pending (failed after a dunning retry) and
is charged again as usual.
Tax rate in percent, for example 18 for 18%.
The time zone to show the dates in, so they match the PDF.
The footer this invoice was issued with.
object
The exact quantity as a decimal string, for example 3 or 1.5.
The unit price in cents at full precision, for example 0.3 for $0.003.
object
object
True when the customer can pay online now: the invoice is finalized, something is owed, no payment is in progress, the seller has Stripe connected for the invoice’s mode, and Stripe’s latest answer for the amount was not that it is below its minimum charge.
Sent only when money is owed but can’t be paid online:
online_payment_unavailable when the seller has no payment
provider connected for this mode, amount_below_minimum when
Stripe’s latest answer refused the amount as below its minimum
charge.
False for a test-mode invoice. The page shows a test-mode banner.
Example
{ "invoice": { "status": "draft", "payment_status": "pending", "billing_timezone": "Asia/Kolkata" }, "pay_unavailable_reason": "online_payment_unavailable"}Unknown token, or the invoice is a draft.
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}Rate limited (60/min/IP)