Create dunning policy
const url = 'http://localhost:8080/v1/dunning/policies';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"name":"example","enabled":false,"retry_days":[3,4,7],"final_subscription_action":"none","final_invoice_action":"none"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:8080/v1/dunning/policies \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "name": "example", "enabled": false, "retry_days": [ 3, 4, 7 ], "final_subscription_action": "none", "final_invoice_action": "none" }'A dunning policy decides how failed payments are retried and what
happens when the retries run out. Every account starts with one
policy per mode, “Default retries”, as the default. The first
enabled policy you create in a mode takes the default from it,
unless you have edited it or made it the default yourself; later
ones do not (use set-default). Customers without an assigned policy
use the default, so check is_default in the response.
enabled defaults to false. A disabled policy starts no dunning, so
send enabled: true for the policy to take effect. A policy created
disabled never takes the default from “Default retries”.
Authorizations
Section titled “Authorizations”Parameters
Section titled “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
A disabled policy starts no dunning.
Days to wait before each retry. The first counts from the failed
payment, or, with no card on file, from the collection that found
none. Each later one counts from the retry before it: [3, 4, 7]
retries on day 3, 7 and 14. The number of retries is the length
of the list. A day is 24 hours. retry_schedule,
max_retry_attempts and grace_period_days are refused.
Example
[ 3, 4, 7]What happens to the subscription when retries run out. pause
pauses collection and sends subscription.collection_paused
with triggered_by: dunning. It does nothing if collection is
already paused, the subscription is canceled, or the invoice
was paid first. cancel cancels the subscription and sends
subscription.canceled with canceled_by: dunning. It does
nothing if the subscription is already canceled. If a scheduled
cancel was already due, that cancel happens instead, and its
event says canceled_by: schedule.
What happens to the invoice when retries run out.
Responses
Section titled “Responses”The new policy
object
Customers without an assigned policy use the default.
True for the “Default retries” policy the account was created with, until you edit it or make it the default. While it is true, the next enabled policy you create becomes the default instead. Set by the server: a request body that sends it is refused with 422.
Days to wait before each retry; the number of retries is its length.
Example
{ "id": "hc_dpol_1a2b3c4d5e6f7890", "retry_days": [ 3, 4, 7 ], "final_subscription_action": "none", "final_invoice_action": "none"}invalid_request: 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" }}validation_error: a replaced field was sent (final_action,
retry_schedule, max_retry_attempts, grace_period_days),
retry_days is missing or out of bounds, or an unknown final
action.
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" }}