Bio pages
A bio page is a single mobile-first landing page hosting a stack of
links — the format Linktree popularised for “link in bio” on Instagram,
TikTok, and X. Each page lives at bio.elido.app/<username> (or a
custom domain) and is rendered server-side by bio-renderer so it
loads on the first paint.
Bio pages are independent from short links: they have their own URL namespace, their own click stream, and their own analytics.
Create a page
curl -X POST \
https://api.elido.app/v1/workspaces/1/bio \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"username": "acme",
"display_name": "Acme Inc.",
"bio": "We make sprockets that just work.",
"avatar_url": "https://cdn.example.com/avatar.png",
"is_published": true,
"theme": {
"bg_color": "#0f172a",
"text_color": "#f8fafc",
"button_style": "rounded",
"button_color": "#5b6cff",
"button_radius": 12
}
}'| Field | Notes |
|---|---|
username | 3–32 chars, lowercase letters, digits, -, _. Globally unique. |
display_name | Bold name shown above the bio. |
bio | Free text below the name. |
avatar_url | Optional image URL; rendered as a circle. |
is_published | Defaults false. Unpublished pages 404 publicly. |
theme | JSON with bg_color, text_color, button_style, button_color, button_radius. |
Add links
curl -X POST \
https://api.elido.app/v1/workspaces/1/bio/9/links \
-H "Authorization: Bearer $ELIDO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Latest blog post",
"url": "https://example.com/blog/sprocket-3",
"icon": "rss",
"sort_order": 0
}'sort_order controls render order ascending; rebalance with PATCH
on each link or rely on the dashboard’s drag-and-drop. Icons are
Lucide names (rss, youtube, instagram, …).
Public render
The page itself comes from the bio-renderer service:
GET https://bio.elido.app/acmeThe renderer fetches GET /v1/bio/acme (public, unauthenticated)
once per request and caches by username for 60 seconds.
Click tracking
Each button has a fire-and-forget tracker:
navigator.sendBeacon(
"https://api.elido.app/v1/bio/acme/click",
JSON.stringify({ link_id: 17 })
);The renderer wires this up automatically — you don’t ship the
JavaScript yourself. The endpoint records Country, User-Agent, and
Referer, then fans out to ClickHouse via the same click-ingester
pipeline as short-link clicks.
Heatmap
Per-link click totals power the dashboard heatmap:
curl https://api.elido.app/v1/workspaces/1/bio/9/heatmap \
-H "Authorization: Bearer $ELIDO_TOKEN"[
{ "bio_link_id": 17, "clicks": 4321 },
{ "bio_link_id": 18, "clicks": 1187 }
]The dashboard renders this as a horizontal bar chart sorted by
sort_order so you can see at a glance which links are pulling weight
and which are dead.
Endpoint reference
| Method | Path |
|---|---|
GET | /v1/workspaces/{ws}/bio (list) |
POST | /v1/workspaces/{ws}/bio (create page) |
PATCH | /v1/workspaces/{ws}/bio/{id} |
DELETE | /v1/workspaces/{ws}/bio/{id} |
GET | /v1/workspaces/{ws}/bio/{id}/links |
POST | /v1/workspaces/{ws}/bio/{id}/links |
PATCH | /v1/workspaces/{ws}/bio/{id}/links/{link_id} |
DELETE | /v1/workspaces/{ws}/bio/{id}/links/{link_id} |
GET | /v1/workspaces/{ws}/bio/{id}/heatmap |
GET | /v1/bio/{username} (public render) |
POST | /v1/bio/{username}/click (public click) |