Skip to content
Hardcount
Run it locally
Pre-1.0 · one maintainer · Stripe and cards only · no SDKs (client libraries) yet · no production users yet. Docs describe v0.9.1 (29 Oct 2026) · What’s new →

Data behind the hosted invoice page

GET
/v1/public/invoices/{token}
curl --request GET \
--url http://localhost:8080/v1/public/invoices/example

Unauthenticated. 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.

token
required
string

The invoice as the page shows it.

Media typeapplication/json

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
invoice
required
object
invoice_number
required
string
status
required

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).

string
Allowed values: draft finalized paid voided uncollectible
payment_status
required

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.

string
Allowed values: pending processing succeeded failed unknown
currency
required
string
subtotal_cents
required
integer format: int64
discount_cents
required
integer format: int64
tax_amount_cents
required
integer format: int64
tax_rate
required

Tax rate in percent, for example 18 for 18%.

number
tax_name
string
tax_reverse_charge
boolean
tax_exempt_reason
string
total_amount_cents
required
integer format: int64
amount_due_cents
required
integer format: int64
amount_paid_cents
required
integer format: int64
credits_applied_cents
required
integer format: int64
issued_at
string format: date-time
due_at
string format: date-time
paid_at
string format: date-time
voided_at
string format: date-time
billing_timezone

The time zone to show the dates in, so they match the PDF.

string
memo
string
footer

The footer this invoice was issued with.

string
line_items
required
Array<object>
object
description
required
string
quantity
required

The exact quantity as a decimal string, for example 3 or 1.5.

string
unit_amount_decimal
required

The unit price in cents at full precision, for example 0.3 for $0.003.

string
meter_unit
string
amount_cents
required
integer format: int64
tax_amount_cents
integer format: int64
total_amount_cents
required
integer format: int64
currency
required
string
bill_to
required
object
name
string
email
string
address_line1
string
address_line2
string
city
string
state
string
postal_code
string
country
string
branding
required
object
company_name
string
company_email
string
company_phone
string
company_address_line1
string
company_address_line2
string
company_city
string
company_state
string
company_postal_code
string
company_country
string
logo_url
string
brand_color
string
support_url
string
pay_enabled
required

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.

boolean
pay_unavailable_reason

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.

string
Allowed values: online_payment_unavailable amount_below_minimum
livemode
required

False for a test-mode invoice. The page shows a test-mode banner.

boolean

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.

Media typeapplication/json
object
error
object
type

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.

string
code
string
message
string
request_id
string
param

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.

string

Example

{
"error": {
"type": "invalid_request_error"
}
}

Rate limited (60/min/IP)