Set up a new installation
const url = 'http://localhost:8080/v1/bootstrap';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"token":"example","tenant_name":"Demo Tenant","owner_email":"admin@hardcount.local","owner_password":"example"}'};
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/bootstrap \ --header 'Content-Type: application/json' \ --data '{ "token": "example", "tenant_name": "Demo Tenant", "owner_email": "admin@hardcount.local", "owner_password": "example" }'One-time setup for a new installation. It works only while no
tenant exists and only when the server has HARDCOUNT_BOOTSTRAP_TOKEN
set. Send that token as token in the body or as
Authorization: Bearer <token>. Never send it in the query string.
In one transaction it creates the first tenant, its owner user, a
test secret key and a live secret key. The
response is the only place the keys appear, and the password too
when Hardcount generated it. The response has Cache-Control: no-store.
Defaults: tenant name “Demo Tenant”, owner email
admin@hardcount.local, and a generated password when
owner_password is empty. The hardcount-bootstrap command does the
same setup from the shell.
Request Body
Section titled “Request Body”object
The value of HARDCOUNT_BOOTSTRAP_TOKEN. Or send it as a Bearer token.
Must contain @.
12 or more characters, at most 72 bytes, and not a common password from a leaked-password list. Omit to have Hardcount generate one.
Responses
Section titled “Responses”Installation set up. Save these credentials; they are not shown again.
object
object
Dashboard login email.
Dashboard login password (the one you sent, or the generated one).
Test-mode secret key (hc_secret_test_...).
Live-mode secret key (hc_secret_live_...).
Example
{ "tenant": { "id": "hc_ten_1a2b3c4d5e6f7890", "status": "active" }}Bootstrap is disabled (HARDCOUNT_BOOTSTRAP_TOKEN not set) or the token is wrong
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" }}A tenant already exists (code already_bootstrapped), or a user
with owner_email already exists (code already_exists).
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" }}owner_email is not an email, or owner_password is too short or too long. Nothing was written.
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" }}Rate limited
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" }}