Reference
API (preview)
Internal server functions used by the web app. A public API is on the roadmap.
Authentication
All authenticated calls carry a Supabase bearer token attached by the client middleware. Row-level security scopes every read and write to auth.uid().
Try it out
Run any endpoint live against your own account. Your session token is attached automatically — sign in first, otherwise every call returns unauthorized. Requests execute for real, so treat write and delete endpoints with care.
Try it out
Not signed in — calls will return 401List your chat threads · /_serverFn/listChats
This endpoint takes no request body.
// Response will appear hereChats
listChats() → Chat[]
createChat({ title }) → Chat
renameChat({ id, title }) → Chat
deleteChat({ id }) → { ok: true }Messages
listMessages({ chatId }) → Message[]
sendMessage({ chatId, content }) → { reply: Message }Profile
getProfile() → Profile
updateProfile({ display_name, avatar_url }) → ProfileTypes
type Chat = {
id: string;
user_id: string;
title: string;
created_at: string; // ISO
updated_at: string; // ISO
};
type Message = {
id: string;
chat_id: string;
role: "user" | "assistant";
content: string;
image_url?: string | null;
created_at: string;
};
type Profile = {
id: string;
display_name: string | null;
avatar_url: string | null;
};Errors
Server functions throw typed errors. Clients receive a JSON body with a stable code.
{ "code": "unauthorized", "message": "Sign in required." }
{ "code": "not_found", "message": "Chat not found." }
{ "code": "forbidden", "message": "You don't own this chat." }
{ "code": "rate_limited", "message": "Too many requests." }
{ "code": "validation_error", "message": "Invalid input.", "issues": [...] }
{ "code": "ai_upstream_error", "message": "Model gateway failed." }Rate limits (preview)
- 60 chat messages / hour / account
- 20 image uploads / hour / account
- 200 read requests / minute / account
Example — send a message
import { sendMessage } from "@/lib/chats.functions";
const { reply } = await sendMessage({
chatId: "c_01H...",
content: "Sharp pain in my lower right abdomen for 6 hours.",
});
console.log(reply.content);Webhooks (roadmap)
A signed webhook will fire on new severe-condition matches so clinicians can be paged. Signature verification uses HMAC-SHA256 over the raw body with a per-tenant secret.
Endpoint reference
Each server function is reachable over HTTPS at the app origin. All requests are JSON and require the Authorization header unless noted.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /_serverFn/listChats | List the caller's chat threads | Required |
| POST | /_serverFn/createChat | Create a thread | Required |
| POST | /_serverFn/renameChat | Rename a thread | Required |
| POST | /_serverFn/deleteChat | Delete a thread and its messages | Required |
| GET | /_serverFn/listMessages | List messages in a thread | Required |
| POST | /_serverFn/sendMessage | Send a message and get the AI reply | Required |
| GET | /_serverFn/getProfile | Read the caller's profile | Required |
| POST | /_serverFn/updateProfile | Update display name or avatar | Required |
Authentication header
Authorization: Bearer <supabase_access_token>
Content-Type: application/jsonPOST /_serverFn/sendMessage — request
{
"data": {
"chatId": "c_01H8ZR3K2QW",
"content": "Sharp pain in my lower right abdomen for 6 hours."
}
}Response — 200
{
"reply": {
"id": "m_01H8ZR3M7TC",
"chat_id": "c_01H8ZR3K2QW",
"role": "assistant",
"content": "Right lower quadrant pain lasting hours can indicate appendicitis...",
"image_url": null,
"created_at": "2026-07-28T10:14:02.881Z"
}
}GET /_serverFn/listChats — response
[
{
"id": "c_01H8ZR3K2QW",
"user_id": "u_9f2a...",
"title": "Abdominal pain",
"created_at": "2026-07-28T10:12:44.010Z",
"updated_at": "2026-07-28T10:14:02.881Z"
}
]Error response — 401
{ "code": "unauthorized", "message": "Sign in required." }curl example
curl -X POST https://homunculuslabs.fun/_serverFn/createChat \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":{"title":"Chest tightness"}}'