Set billing thresholds
const url = 'http://localhost:8080/v1/subscriptions/example/billing-thresholds';const options = { method: 'PUT', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"amount_gte":1,"item_thresholds":[{"subscription_item_id":"example","usage_gte":"example"}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url http://localhost:8080/v1/subscriptions/example/billing-thresholds \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "amount_gte": 1, "item_thresholds": [ { "subscription_item_id": "example", "usage_gte": "example" } ] }'Sets the billing thresholds: limits that make Hardcount invoice before
the period ends. A threshold bills sooner; it does not cap or
block usage. Set amount_gte, item_thresholds, or both. The
first threshold reached triggers the invoice.
The threshold invoice bills the period’s usage so far, and no base
fee. The period keeps its dates, the base fee is billed as it would
be with no threshold, and the period’s next invoice bills the rest
of the usage.
reset_billing_cycle, which started a new period at the threshold
invoice, was removed; a body that sends it is refused with 422.
Each call replaces the whole configuration. Item thresholds left out of the body are removed.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Header Parameters
Section titled “Header Parameters”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.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Invoice early when the period’s usage so far reaches this amount, in cents of the subscription’s currency. The base fee is not counted.
object
An item of this subscription. Each item can appear once.
Invoice early when the item’s usage in the period reaches this quantity. A decimal string (e.g. “1000” or “3.14”): more than 0, under 10^26, at most 12 decimal places (trailing zeros do not count), and at most 100 characters.
Example generated
{ "amount_gte": 1, "item_thresholds": [ { "subscription_item_id": "example", "usage_gte": "example" } ]}Responses
Section titled “Responses”The subscription with billing_thresholds set.
object
The subscription’s priced lines. Each item names its plan.
One priced line of a subscription. It pairs a plan with a quantity.
object
The item’s stored metadata. Absent when empty.
object
Plan the item moves to at pending_plan_effective_at. Absent when no change is scheduled.
When the plan was last changed immediately. Absent until the first such change.
When the subscription was canceled. Absent until then.
A scheduled cancel time, if one is set.
True when the subscription cancels at the end of the current period.
When a scheduled cancel takes effect. Absent when no cancel is scheduled.
Present on a subscription while collection is paused. New invoices are still created, but stay drafts and are not charged.
object
When collection resumes. Absent when the pause lasts until it is cleared. On a pause set by dunning, it is when Hardcount recorded the payment of the invoice the pause was set for.
Who paused collection. operator: you, through the API or the
dashboard; the pause lasts until you resume it or until
resumes_at. dunning: a dunning policy’s pause final action.
A dunning pause lifts by itself once the invoice whose dunning
set it is paid, even if another invoice is still owed. Periods
that ended during the pause keep their draft invoices.
Limits that make Hardcount invoice before the period ends. A threshold invoice bills the period’s usage so far, and no base fee. The period keeps its dates, the base fee is billed as it would be with no threshold, and the period’s next invoice bills the rest of the usage.
object
Usage threshold in cents, compared with the period’s usage so far (the base fee is not counted). Absent when not set.
object
Usage quantity threshold, as a decimal string.
End of the current period (exclusive).
The current period as text, with the last day included, in the tenant’s billing time zone.
Day of the month the subscription bills on (1-31). Absent for calendar-monthly subscriptions.
Present on a subscription while the billing run is failing on the
work that subscription has due now (a period close, or a scheduled
mid-period cancel). Absent otherwise: a subscription that bills
cleanly, or whose due work changed, carries no billing_failure.
After each failed attempt the billing run waits before the next one:
15 minutes, 1 hour, 4 hours, 12 hours, then 24 hours three times.
The eighth failure parks the subscription (parked: true, no
next_attempt_at). A parked subscription is not attempted again
until POST /v1/subscriptions/{id}/retry-billing is called.
Never set on a subscription attached to a test clock.
object
Failed attempts on the work that is due now. At least 1.
The attempt at which the subscription parks (8).
Why the latest attempt failed: a sentence an operator can act on, or a generic line carrying the subscription id as the reference to search the server log for. Never raw error text.
When the latest attempt failed.
The billing run does not attempt the subscription before this instant. The attempt happens on the first run at or after it, so on an hourly schedule it can be up to an hour later. Absent when parked.
True when automatic attempts have stopped.
Example
{ "status": "draft", "billing_time": "calendar", "pause_collection": { "behavior": "keep_as_draft", "set_by": "operator" }}The body is not valid JSON.
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" }}Subscription not found.
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" }}Code invalid_state: the subscription is canceled.
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" }}Neither amount_gte nor item_thresholds is set;
amount_gte is negative; an item id is missing, repeated, or
not on this subscription; a usage_gte is not a number or is
outside its limits (param is item_thresholds[i].usage_gte);
or the subscription’s plans use more than one currency.
Also: the body has an unknown field (param names it; the body must use only the fields listed here).
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" }}