Skip to content

Domains

Manage the domains a HappyView instance serves. Each domain gets its own atproto OAuth client identity. The primary domain follows PUBLIC_URL: every start makes the domain with that URL primary, adding it on the first start. All endpoints require the settings:manage permission or the platform’s key.

On an instance run by a managed-hosting platform (one with PLATFORM_API_KEY_HASH set, which GET /config reports as platform_managed), the platform decides which hostnames reach the instance and which one is its public URL. There, adding, deleting and making a domain primary accept only the platform’s key, and answer 403 to everyone else, an operator with superadmin included. Listing domains still accepts settings:manage.

const TOKEN = "hv_..."; // your API key
const headers = { Authorization: `Bearer ${TOKEN}` };
GET /admin/domains
interface Domain {
id: string;
url: string;
is_primary: boolean;
created_at: string;
updated_at: string;
}
const response = await fetch("http://127.0.0.1:3000/admin/domains", {
headers,
});
const data: Domain[] = await response.json();

Response: 200 OK

[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://gamesgamesgamesgames.games",
"is_primary": true,
"created_at": "2026-04-16T00:00:00Z",
"updated_at": "2026-04-16T00:00:00Z"
}
]
POST /admin/domains
interface Domain {
id: string;
url: string;
is_primary: boolean;
created_at: string;
updated_at: string;
}
const response = await fetch("http://127.0.0.1:3000/admin/domains", {
method: "POST",
headers: {
...headers,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://api.example.com",
}),
});
const data: Domain = await response.json();
FieldTypeRequiredDescription
urlstringyesAn origin: scheme, host and optional port, with no path. A trailing slash is dropped, and the scheme and host are lowercased. Must be https unless PUBLIC_URL is a loopback address.

Returns 400 Bad Request if the URL is invalid, has a path or no host, isn’t https where that’s required, or is already registered.

Response: 201 Created

{
"id": "550e8400-e29b-41d4-a716-446655440001",
"url": "https://api.example.com",
"is_primary": false,
"created_at": "2026-04-16T00:00:00Z",
"updated_at": "2026-04-16T00:00:00Z"
}

Also builds an OAuth client for the domain and updates the in-memory cache.

DELETE /admin/domains/{id}
await fetch("http://127.0.0.1:3000/admin/domains/550e8400-e29b-41d4-a716-446655440001", {
method: "DELETE",
headers,
});

Returns 400 Bad Request if the domain is the primary domain (make another domain primary first), or if it’s the instance’s only domain. Returns 404 Not Found if the domain doesn’t exist.

Response: 204 No Content

Also removes the domain’s OAuth client and cache entry.

POST /admin/domains/{id}/primary
await fetch("http://127.0.0.1:3000/admin/domains/550e8400-e29b-41d4-a716-446655440001/primary", {
method: "POST",
headers,
});

Sets the target domain as the primary. Unsets the current primary in a single operation. Returns 404 Not Found if the domain doesn’t exist. The change lasts until the next start, which makes the domain matching PUBLIC_URL primary again, so change PUBLIC_URL too.

Response: 204 No Content

Also updates the in-memory cache and primary client reference.