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 →

Set the Stripe webhook signing secret

PATCH
/v1/settings/stripe/{mode}/webhook
curl --request PATCH \
--url http://localhost:8080/v1/settings/stripe/test/webhook \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "webhook_secret": "example" }'

Saves the whsec_ signing secret for a mode that is already connected, without entering the API keys again. Hardcount does not call Stripe here; the secret is checked on each incoming webhook. Writes an audit row (never the secret).

mode
required
string
Allowed values: test live
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.

Media typeapplication/json
object
webhook_secret
required

Must start with whsec_.

string

Example generated

{
"webhook_secret": "example"
}

Updated connection

Media typeapplication/json
object
id
required

Also the {id} in the Stripe webhook URL /v1/webhooks/stripe/{id}.

string
tenant_id
required
string
livemode
required
boolean
stripe_account_id

Set after Stripe accepted the key.

string
stripe_account_name
string
secret_key_prefix

First 12 characters of the secret key.

string
secret_key_last4
required
string
publishable_key
required
string
webhook_secret_last4
string
has_webhook_secret
required
boolean
verified_at

Last time Stripe accepted the key.

string format: date-time
last_verified_error

Why the last Stripe check failed, in Stripe’s own words (or the network error when Stripe was not reached). Empty when it passed.

string
last_verified_error_kind

What kind of failure last_verified_error is. rejected: Stripe answered and refused the key (invalid, revoked, or without access). unavailable: Stripe was not reached, or answered 429/5xx, so the key was not checked; connecting again may work. Absent when the check passed, and on keys that failed before this field existed.

string
Allowed values: rejected unavailable
created_at
required
string format: date-time
updated_at
required
string format: date-time
retries_queued

Connect response only. Number of stuck invoices being retried in the background.

integer

Example

{
"last_verified_error_kind": "rejected"
}

mode is not test or live, or the body is not valid JSON

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 is not connected for this mode. Connect it first.

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

webhook_secret is missing or does not start with whsec_

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