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 →

List a customer's credit ledger

GET
/v1/credits/ledger/{customer_id}
curl --request GET \
--url 'http://localhost:8080/v1/credits/ledger/example?entry_type=grant&dir=asc&limit=50&offset=0' \
--header 'Authorization: Bearer <token>'

Ledger entries for one customer. balance_after is the running balance in the entry’s currency, in time order over the whole ledger, so it stays right when you filter by entry_type.

customer_id
required
string
entry_type
string
Allowed values: grant usage expiry adjustment
sort
string

One of created_at (default), amount_cents, entry_type, expires_at.

dir
string
Allowed values: asc desc

Default desc.

limit
integer
default: 50 >= 1 <= 100

Rows per page, from 1 to 100. Default 50. Any other value, or one that is not a whole number, is a 422 with param: limit.

offset
integer
0

Rows to skip, 0 or more. Default 0. A negative value, or one that is not a whole number, is a 422 with param: offset.

Ledger entries. total counts entries matching the filter.

Media typeapplication/json
object
data
required
Array<object>
object
id
required
string
tenant_id
string
customer_id
required
string
entry_type
required
string
Allowed values: grant usage expiry adjustment
currency
required

The entry’s currency, uppercase. Credit pays only invoices in its own currency.

string
amount_cents
required

Positive adds credit, negative removes it.

integer format: int64
balance_after
required

The customer’s credit balance in this entry’s currency after it, in cents.

integer format: int64
description
required
string
invoice_id
string
expires_at

On a grant, its expiry. The credit can be drawn up to and including this instant; a period invoice draws it when its period ends at or before it. The dashboard sends the local midnight after the picked day.

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

For entries that add credit, how much has been used or expired.

integer format: int64
grant_kind
string
Allowed values: commit promotional
source_invoice_id

For a commit grant, the invoice that paid for it.

string
source_subscription_id

With source_subscription_item_id, source_plan_changed_at and source_change_type, set on a proration credit: the subscription change that produced it.

string
source_subscription_item_id
string
source_plan_changed_at
string format: date-time
source_change_type

Classifies per-item proration artifacts so the dedup index can distinguish a plan-change and a quantity-change that happen to share the same (subscription, item, timestamp).

string
Allowed values: plan quantity add remove
source_credit_note_id

The credit note that granted this credit to the balance.

string
source_invoice_reversal_id

The voided invoice whose applied credit this entry gave back.

string
total
required

How many rows match the request’s filters, on every page, not only this one. The audit log and the customer list’s search and name or email sort say where they stop counting.

integer

Example

{
"data": [
{
"entry_type": "grant",
"grant_kind": "commit",
"source_change_type": "plan"
}
]
}

No customer with this ID in this tenant and mode.

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

limit or offset is out of range or not a whole number, or the request sends the removed after cursor (param names it)

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