A stored instance setting, such as the app name, ToS URL, privacy policy URL or logo, takes precedence over its environment variable while the instance runs. All endpoints require the settings:manage permission.
const TOKEN = "hv_..."; // your API keyconst headers = { Authorization: `Bearer ${TOKEN}` };const TOKEN = "hv_..."; // your API keyconst headers = { Authorization: `Bearer ${TOKEN}` };let token = "hv_..."; // your API keyconst token = "hv_..." // your API key# All examples assume $TOKEN is an API key (hv_...)AUTH="Authorization: Bearer $TOKEN"List settings
Section titled “List settings”GET /admin/settingsconst response = await fetch("http://127.0.0.1:3000/admin/settings", { headers,});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/admin/settings", { headers,});const data = await response.json();let response = client .get("http://127.0.0.1:3000/admin/settings") .bearer_auth(token) .send() .await?;let data: serde_json::Value = response.json().await?;req, _ := http.NewRequest("GET", "http://127.0.0.1:3000/admin/settings", nil)req.Header.Set("Authorization", "Bearer "+token)resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl http://127.0.0.1:3000/admin/settings -H "$AUTH"Response: 200 OK
[ { "key": "app_name", "value": "My HappyView", "source": "database" }]Stored values come first, sorted by key. They include every stored row, such as the logo_data and logo_content_type rows the logo upload writes. They are followed by values from the environment or happyview.toml for known keys with no stored value; a key with neither appears nowhere, even when it has a default. source is database, env or project.
Known settings
Section titled “Known settings”| Key | Env var | Default | Description |
|---|---|---|---|
app_ | APP_NAME | --- | Application name shown in sidebar and OAuth consent screen |
client_ | CLIENT_URI | --- | Public URL for this instance, linked from OAuth consent screen |
logo_ | LOGO_URI | --- | External URL to a logo image |
tos_ | TOS_URI | --- | Link to terms of service |
policy_ | POLICY_URI | --- | Link to privacy policy |
backfill_ | BACKFILL_CONCURRENT_PDS | 10 | How many PDS servers to fetch from simultaneously during backfill |
backfill_ | BACKFILL_CONCURRENT_DIDS_PER_PDS | 3 | How many accounts to fetch concurrently from each PDS |
backfill_ | BACKFILL_CONCURRENT_RESOLUTION | 100 | How many DID document lookups to run in parallel during PDS resolution |
backfill_ | BACKFILL_RETENTION_DAYS | 28 | Days to keep per-account detail from completed backfill jobs. 0 = keep indefinitely |
verbose_ | VERBOSE_EVENT_LOGGING | false | Log every record index, record skip, and successful script run (record, label, and XRPC) to the event log. "true" in any case turns it on, and a change takes up to 30 seconds to apply. High write volume, so recommended only for debugging |
feature.spaces_ | FEATURE_SPACES_ENABLED | --- | Enables the experimental Permissioned Spaces API. When "true" (in any case), space endpoints are available. When absent or any other value, space endpoints return 404 FeatureDisabled |
feature.spaces_ | --- | --- | When "true" (in any case), a user signing in with a space: scope granted gets a job enqueued to move the space repos HappyView keeps for them to their PDS |
event_ | EVENT_LOG_RETENTION_DAYS | 30 | Days to keep event log entries and job logs. 0 = keep indefinitely |
script_ | SCRIPT_INSTRUCTION_LIMIT | 1000000 | Instruction budget for a script run. PUT refuses a value outside 1000 to 1000000000 with 400 Bad Request |
script_ | SCRIPT_WALL_CLOCK_SECONDS | 10 | Wall-clock limit for a request’s script run, in seconds. PUT refuses a value outside 1 to 300 with 400 Bad Request |
script_ | SCRIPT_METRICS_ENABLED | true | Record per-script run metrics for /admin/metrics. PUT refuses anything but "true" or "false" with 400 Bad Request |
Upsert a setting
Section titled “Upsert a setting”PUT /admin/settings/{key}const response = await fetch("http://127.0.0.1:3000/admin/settings/app_name", { method: "PUT", headers: { ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ value: "My HappyView" }),});const response = await fetch("http://127.0.0.1:3000/admin/settings/app_name", { method: "PUT", headers: { ...headers, "Content-Type": "application/json", }, body: JSON.stringify({ value: "My HappyView" }),});let response = client .put("http://127.0.0.1:3000/admin/settings/app_name") .bearer_auth(token) .json(&serde_json::json!({ "value": "My HappyView" })) .send() .await?;body := bytes.NewBufferString(`{ "value": "My HappyView" }`)req, _ := http.NewRequest("PUT", "http://127.0.0.1:3000/admin/settings/app_name", body)req.Header.Set("Authorization", "Bearer "+token)req.Header.Set("Content-Type", "application/json")resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl -X PUT http://127.0.0.1:3000/admin/settings/app_name \ -H "$AUTH" \ -H "Content-Type: application/json" \ -d '{ "value": "My HappyView" }'The body is { "value": "..." }. The value is always a string, so send "true", not true. Any key is accepted; the table above lists the keys HappyView reads. logo, xrpc-proxy and telemetry are routes of their own, so PUT /admin/settings/telemetry reaches the telemetry endpoint, not this one.
Response: 204 No Content. 400 Bad Request when a script limit is out of range or script_metrics_enabled isn’t "true" or "false". A telemetry.* key returns 409 Conflict where a hosting platform manages telemetry.
Delete a setting
Section titled “Delete a setting”DELETE /admin/settings/{key}Removes the override; the corresponding environment variable or happyview.toml value (if any) takes effect again. Returns 404 Not Found if no value is stored for that key, and 409 Conflict for a telemetry.* key where a hosting platform manages telemetry.
Response: 204 No Content
Database info
Section titled “Database info”GET /admin/settings/db-infoReturns database backend, connection pool sizes, and whether an instance restart is recommended to resize the backfill pool for current concurrency settings.
const response = await fetch("http://127.0.0.1:3000/admin/settings/db-info", { headers,});const data = await response.json();const response = await fetch("http://127.0.0.1:3000/admin/settings/db-info", { headers,});const data = await response.json();let client = reqwest::Client::new();let response = client .get("http://127.0.0.1:3000/admin/settings/db-info") .bearer_auth(token) .send() .await?;let data: serde_json::Value = response.json().await?;req, _ := http.NewRequest("GET", "http://127.0.0.1:3000/admin/settings/db-info", nil)req.Header.Set("Authorization", "Bearer "+token)resp, err := http.DefaultClient.Do(req)if err != nil { log.Fatal(err)}defer resp.Body.Close()curl http://127.0.0.1:3000/admin/settings/db-info -H "$AUTH"Response: 200 OK
{ "backend": "sqlite", "server_max_connections": null, "main_pool_size": 32, "backfill_pool_size": 16, "restart_recommended": false}| Field | Type | Description |
|---|---|---|
backend | string | "sqlite" or "postgres" |
server_ | number | null | Postgres max_connections setting. null for SQLite |
main_ | number | Current main connection pool size |
backfill_ | number | Current backfill connection pool size |
restart_ | boolean | true if concurrency settings have changed and a restart would resize the pool |
Upload or delete the logo
Section titled “Upload or delete the logo”PUT /admin/settings/logoDELETE /admin/settings/logoPUT takes a multipart/form-data body whose first part is the image, and stores it as the instance logo. The logo is served publicly at GET /settings/logo, and is used in place of logo_uri while it is stored. The part’s content type must be image/* and it must be at most 5 MB, otherwise the request is refused with 400 Bad Request. DELETE removes the stored logo.
Response: 204 No Content for both.
© HappyProto

