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 →

Retry a failed credit-note refund

POST
/v1/credit-notes/{id}/retry-refund
curl --request POST \
--url http://localhost:8080/v1/credit-notes/example/retry-refund \
--header 'Authorization: Bearer <token>'

Sends the refund part of an issued credit note to Stripe again, when its refund_status is failed, unknown, pending or none. Before creating a refund, Hardcount asks Stripe for one already made for this note and records it if found. For unknown and none, Hardcount then resends the earlier request whose result it never recorded, with the same idempotency key. While Stripe keeps that key (at least 24 hours), Stripe returns the earlier request’s result instead of making a second refund.

id
required
string
Idempotency-Key
string

Makes the call safe to retry. Send a unique value (a UUID works) and the same value on every retry. For 24 hours, a repeat with the same key, method, path and body gets the first response again, with an Idempotent-Replayed: true header, instead of running again. The error cases are in docs/api-surface.md. The usage routes do not take it: each usage event carries its own idempotency_key.

Credit note with its updated refund_status.

Media typeapplication/json

A credit note reduces what a customer owes on an invoice, or gives money back after it was paid. Its total is split three ways: refund to the card (refund_amount_cents), credit to the customer’s balance (credit_amount_cents), and money returned outside Hardcount (out_of_band_amount_cents).

object
id
required
string
tenant_id
string
invoice_id
required
string
customer_id
required
string
credit_note_number
required
string
status
required
string
Allowed values: draft issued voided
reason
required
string
subtotal_cents
required
integer format: int64
tax_amount_cents
required
integer format: int64
total_cents
required
integer format: int64
refund_amount_cents
required
integer format: int64
credit_amount_cents
required
integer format: int64
out_of_band_amount_cents
required
integer format: int64
commit_retired_cents

Prepaid commit credits this note retired. Absent on other notes.

integer format: int64
currency
required
string
issued_at
string format: date-time
voided_at
string format: date-time
refund_status
required

The refund part’s state at Stripe. unknown: Stripe gave no clear answer (a timeout, a network error, a Stripe 5xx, a 409 or a 429), so the customer may already be refunded. Don’t refund it again in the Stripe Dashboard. Retry it with POST /v1/credit-notes/{id}/retry-refund, which looks for the refund at Stripe first and then resends the same request with the same idempotency key.

string
Allowed values: none pending succeeded failed unknown
stripe_refund_id
string
bill_to_snapshot

What an issued invoice or credit note prints about its buyer, copied when the document was issued (ADR-109). The printed name is legal_name when set, otherwise display_name.

object
display_name
required
string
legal_name
string
email
string
address_line1
string
address_line2
string
city
string
state
string
postal_code
string
country
string
tax_id

On an invoice, the tax ID its tax was calculated with when set, otherwise the billing profile’s tax ID at issue.

string
tax_id_type
string
supplier_snapshot

What an issued invoice or credit note prints about its seller (the tenant’s company), copied when the document was issued (ADR-109).

object
name
string
email
string
phone
string
address_line1
string
address_line2
string
city
string
state
string
postal_code
string
country
string
tax_id
string
tax_transaction_id

Tax provider’s reversal transaction, set when the note is issued.

string
is_simulated
required

True when issued_at is test-clock time.

boolean
issue_pending
required

True on a draft that Hardcount will issue automatically.

boolean
tax_reversal_pending
required

True when the tax reversal failed and will be retried.

boolean
metadata
object
key
additional properties
any
created_at
required
string format: date-time
recorded_at

Real-world time the row was written, when created_at is test-clock time.

string format: date-time
updated_at
required
string format: date-time

Example

{
"status": "draft",
"refund_status": "none"
}

Credit note not found.

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"
}
}

Code invalid_state. The note is not issued, has no refund part, the refund already succeeded or is still processing at Stripe, Stripe is not connected, or the invoice was paid outside Stripe (void the note and issue a new one with out_of_band_amount_cents).

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"
}
}

Stripe refused the refund (code provider_error): the note’s refund_status is failed. Or Stripe gave no clear answer (code payment_outcome_unknown): it is unknown, and the customer may already be refunded. Retry later in both cases.

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"
}
}