Social profile
Manage your profile social section: outbound links, layout widgets, section theme, and display order. Read a friend's filtered section when you share an accepted friendship.
Base path: /api/v1/social (your section), /api/v1/friends/{friendUserId}/social (friend view). Scope: social: read for GET routes, read_write for mutations. See Authentication and Scopes.
Guestbook and poll widget interactions use separate scopes under /api/v1/social/widgets/{widgetId}/guestbook and .../poll: see Guestbook & poll.
Spotify connection, now-playing, and playlists are not available on public REST. Gallery image upload for custom_badge widgets is not available on REST: use hosted gallery URLs in widget config.
Concepts
- Social section: links, widgets, musicConnections, optional theme, and updatedAt for the account owner.
- Visibility: each link and widget: public, logged_in, friends_only, mutuals_only, private. Friend reads apply filterSocialSectionForViewer server-side.
- Upsert: POST creates; PATCH .../{id} updates by path id. Optional id in body supports create-with-id.
- Widget type: top-level type must equal config.kind.
- Clerk OAuth links: POST /sync-oauth inserts/updates verified Clerk identities (Discord, TikTok, YouTube, Twitch, GitHub). New rows default to visibility: private until you set visibility via link PATCH.
- Spotify platform: manual platform: spotify links are rejected; connect via Settings → Connections, not REST.
Endpoint index
| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/social | read |
| PATCH | /api/v1/social | read_write: theme only |
| POST | /api/v1/social/links | read_write |
| PATCH / DELETE | /api/v1/social/links/{linkId} | read_write |
| POST | /api/v1/social/widgets | read_write |
| PATCH / DELETE | /api/v1/social/widgets/{widgetId} | read_write |
| POST | /api/v1/social/reorder | read_write |
| POST | /api/v1/social/sync-oauth | read_write |
| GET | /api/v1/friends/{friendUserId}/social | read |
Get my social section
GET/api/v1/social
200: 200: full owner section (all visibilities). No query parameters.
curl -sS \
-H "Authorization: Bearer YOUR_TOKEN" \
"https://api.floralitys.com/api/v1/social"{
"userId": "a0a0a0a0-a0a0-a0a0-a0a0-a0a0a0a0a0a0",
"links": [
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"platform": "discord",
"displayName": "River",
"handle": "river_pl",
"url": "https://discord.com/users/…",
"description": "Connected via Clerk",
"badge": "main",
"accentColor": null,
"avatarUrl": "https://img.clerk.com/…",
"visibility": "friends_only",
"featured": false,
"sortOrder": 0,
"createdAt": 1714233600000,
"updatedAt": 1714237200000
}
],
"widgets": [
{
"id": "b47ac10b-58cc-4372-a567-0e02b2c3d480",
"type": "status",
"title": "Now",
"config": { "kind": "status", "statusText": "At work" },
"visibility": "public",
"size": "medium",
"sortOrder": 0,
"pinned": false,
"collapsed": false,
"createdAt": 1714233600000,
"updatedAt": 1714237200000
}
],
"musicConnections": [
{
"id": "c47ac10b-58cc-4372-a567-0e02b2c3d481",
"provider": "spotify",
"displayName": "River",
"externalUserId": "spotify-user-id",
"status": "connected",
"connectedAt": 1714233600000,
"updatedAt": 1714237200000
}
],
"theme": {
"accentColor": "#7c3aed",
"borderStyle": "subtle",
"cornerStyle": "rounded"
},
"updatedAt": 1714237200000
}Update section theme
PATCH/api/v1/social
204: 204: updates social_section_settings.theme only. Send null to clear theme. Partial objects merge allowed (all fields optional).
| Field | Values |
|---|---|
cardBackground | string, max 64 |
accentColor | string, max 32 |
borderStyle | default | subtle | strong |
cornerStyle | rounded | sharp |
iconStyle | default | minimal | filled |
curl -sS -X PATCH \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accentColor": "#7c3aed",
"borderStyle": "subtle",
"cornerStyle": "rounded"
}' \
"https://api.floralitys.com/api/v1/social"null// 204 No Content: empty response bodySocial links
POST/api/v1/social/links: 201 { "linkId" }
PATCH/api/v1/social/links/{linkId}: 200 { "linkId" }
DELETE/api/v1/social/links/{linkId}: 204
Link body
| Field | Required | Notes |
|---|---|---|
platform | yes | discord, tiktok, instagram, x, bluesky, youtube, twitch, steam, xbox, playstation, nintendo, github, reddit, pinterest, roblox, vrchat, anilist, myanimelist, letterboxd, spotify (rejected: use Spotify connection), custom |
displayName | yes | 1–120 chars |
handle | no | max 120 |
url | no | valid URL |
description | no | max 280 |
badge | no | max 32 |
accentColor | no | max 32 |
avatarUrl | no | URL, max 2048 (OAuth avatars) |
visibility | yes | visibility enum |
featured | no | default false |
sortOrder | no | int ≥ 0; auto-assigned on create if omitted |
curl -sS -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"platform": "bluesky",
"displayName": "River",
"handle": "river.bsky.social",
"url": "https://bsky.app/profile/river.bsky.social",
"visibility": "friends_only",
"featured": true
}' \
"https://api.floralitys.com/api/v1/social/links"{
"linkId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}Social widgets
POST/api/v1/social/widgets: 201 { "widgetId" }
PATCH/api/v1/social/widgets/{widgetId}: 200 { "widgetId" }
DELETE/api/v1/social/widgets/{widgetId}: 204
Widget body
| Field | Notes |
|---|---|
type | Must match config.kind |
title | Optional, max 120 |
config | Discriminated union on kind (see table below) |
visibility | visibility enum |
size | small | medium | large | full |
pinned / collapsed | default false |
sortOrder | optional int ≥ 0 |
Widget kinds
kind | Config highlights |
|---|---|
status | statusText (1–280) |
discord_server | serverName, optional inviteUrl |
currently_playing | provider (spotify), trackTitle, artistName, artworkUrl |
music | Spotify resource fields, widgetKind, displayStyle, optional memberId |
quote | quote, optional author |
pronouns_identity | pronouns, optional identity |
mood | mood, optional note |
custom_badge | imageUrls[]: hosted gallery URLs only (max 24) |
link_collection | linkIds[]: UUIDs of your links |
media_favourites | entries[] with label + value |
guestbook | allowAnonymous: messages via guestbook routes |
mutuals | maxItems (1–40) |
countdown | targetAt (ms), label |
poll | question, options[] (2–8), optional allowMultipleSelections: votes via poll routes |
sticker_board | stickerUrls[] (max 100 URLs) |
shrine | title, body |
curl -sS -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "status",
"title": "Now",
"config": { "kind": "status", "statusText": "Building integrations" },
"visibility": "public",
"size": "medium",
"pinned": false,
"collapsed": false
}' \
"https://api.floralitys.com/api/v1/social/widgets"{
"widgetId": "b47ac10b-58cc-4372-a567-0e02b2c3d480"
}Reorder links and widgets
POST/api/v1/social/reorder
204: 204: set sortOrder from array index. Provide linkIds and/or widgetIds (at least one). Each array must list every existing id for that resource type: exact permutation, no duplicates, no missing ids.
curl -sS -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"linkIds": ["LINK_UUID_A", "LINK_UUID_B"],
"widgetIds": ["WIDGET_UUID_A", "WIDGET_UUID_B"]
}' \
"https://api.floralitys.com/api/v1/social/reorder"// 204 No Content: empty response bodySync OAuth links from Clerk
POST/api/v1/social/sync-oauth
200: 200: reads verified Clerk external accounts for the API key's Clerk user; inserts missing platform rows or updates avatar/handle/url on existing rows. Empty body. Counts returned:
curl -sS -X POST \
-H "Authorization: Bearer YOUR_TOKEN" \
"https://api.floralitys.com/api/v1/social/sync-oauth"{
"inserted": 2,
"updated": 1
}Get friend social section
GET/api/v1/friends/{friendUserId}/social
200: 200: same SocialSection shape as your section, filtered for friend visibility. Requires accepted friendship; blocks return NOT_FOUND. friendUserId is the owner account UUID (Friends).
curl -sS \
-H "Authorization: Bearer YOUR_TOKEN" \
"https://api.floralitys.com/api/v1/friends/FRIEND_USER_UUID/social"Errors
JSON { code, message }:
UNAUTHORIZED (401): missing/invalid API keyFORBIDDEN (403): insufficient social scopeBAD_REQUEST (400): validation, reorder mismatch, Spotify manual link, type/kind mismatchNOT_FOUND (404): invalid UUID path param; friend not found / not acceptedPRECONDITION_FAILED (503): DATABASE_URL not configured
See Errors.
{
"code": "BAD_REQUEST",
"message": "Link reorder set must match existing links."
}