Skip to content

Main App API

These endpoints power the main wisp.place backend (Bun + Elysia). Responses below are the shapes returned by the handlers in apps/main-app/src/routes/*.

Notes:

  • Authenticated routes rely on the signed did cookie. If authentication fails, the handler throws and Elysia returns an error response.
  • Admin routes rely on the signed admin_session cookie. Unauthorized requests return 401 { error: 'Unauthorized' }.

Redirects to the AT Protocol OAuth authorize URL.

  • 302 → OAuth URL
  • 302/?error=missing_handle if no handle/PDS provided
  • 302/?error=auth_failed on failure
{ "url": "https://..." }

On failure:

{ "error": "Authentication failed", "details": "..." }

Redirects after OAuth completes.

  • 302/onboarding (no sites or domain)
  • 302/editor (existing user)
  • 302/?error=auth_failed on failure
{ "success": true }

On failure:

{ "error": "Logout failed" }

Authenticated:

{ "authenticated": true, "did": "did:plc:..." }

Not authenticated:

{ "authenticated": false }
{
"did": "did:plc:...",
"hasSites": true,
"hasDomain": false,
"domain": null,
"sitesCount": 3
}
{ "did": "did:plc:...", "handle": "user.bsky.social" }
{ "sites": [/* site rows */] }
{
"wispDomains": [{ "domain": "name.wisp.place", "rkey": "site-rkey" }],
"customDomains": [/* custom domain rows */]
}
{ "success": true, "synced": 2, "errors": [] }
{ "rkey": "site-rkey", "domains": [/* domain rows */] }
{ "available": true, "domain": "name.wisp.place" }

Invalid handle:

{ "available": false, "reason": "invalid" }

Registered:

{ "registered": true, "type": "wisp", "domain": "name.wisp.place", "did": "did:plc:...", "rkey": "site-rkey" }

Custom domain:

{ "registered": true, "type": "custom", "domain": "example.com", "did": "did:plc:...", "rkey": "site-rkey", "verified": true }

Unregistered:

{ "registered": false }

Missing domain:

{ "error": "Domain parameter required" }
{ "success": true, "domain": "name.wisp.place" }
{ "success": true, "domain": "name.wisp.place" }
{ "success": true, "id": "abcdef1234567890", "domain": "example.com", "verified": false }
{ "success": true, "verified": true, "error": null, "found": true }
{ "success": true }
{ "success": true }
{ "success": true }
{ "success": true }
{ "success": true, "message": "Site deleted successfully" }

On failure:

{ "success": false, "error": "..." }

Returns the place.wisp.settings record when present, otherwise defaults:

{ "indexFiles": ["index.html"], "cleanUrls": false, "directoryListing": false }

On failure:

{ "success": false, "error": "..." }
{ "success": true, "uri": "at://...", "cid": "bafy..." }

On validation failure:

{ "success": false, "error": "Only one of spaMode, directoryListing, or custom404 can be enabled" }

Server-sent events stream for upload progress.

  • event: progress{ status, progress, result, error }
  • event: doneresult
  • event: error{ error }

Errors:

{ "error": "Job not found" }
{ "error": "Unauthorized" }

Empty upload (no files):

{ "success": true, "uri": "at://...", "cid": "bafy...", "fileCount": 0, "siteName": "my-site" }

Async upload:

{ "success": true, "jobId": "...", "message": "Upload started. Connect to /wisp/upload-progress/..." }
{ "success": true }

Invalid credentials (401):

{ "error": "Invalid credentials" }
{ "success": true }

Authenticated:

{ "authenticated": true, "username": "admin" }

Not authenticated:

{ "authenticated": false }
{ "logs": [/* combined logs */] }
{ "errors": [/* combined errors */] }
{ "overall": {}, "mainApp": {}, "hostingService": {}, "timeWindow": 3600000 }
{ "stats": {}, "recentSites": [], "recentDomains": [] }

Returns the hosting service cache stats payload or:

{ "error": "Failed to fetch cache stats from hosting service", "message": "Hosting service unavailable" }
{ "sites": [/* sites */], "customDomains": [/* domains */] }
{
"uptime": 12345,
"memory": { "heapUsed": 123, "heapTotal": 456, "rss": 789 },
"timestamp": "2026-01-22T00:00:00.000Z"
}