--- title: "DevPlace REST: Profiles and the Social Graph" description: "Profile reads and updates, API-key regeneration, avatar and award rendering, per-user AI correction, modifier, Telegram and notification settings, and the follow, block and mute endpoints with their follower and following listings." language: null framework: null category: api_design tags: - devplace - api - http-api - profiles - social-graph - follow - block - mute - rest - settings - leaderboard keywords: - devplace GET /profile/{username} json - devplace POST /profile/regenerate-api-key - devplace follow unfollow block mute endpoints - devplace followers following list api - devplace avatar award image endpoint last_updated: 2026-08-12 difficulty: intermediate version: "DevPlace (devplace.net), documented 2026-08" related: - rest_search_and_lookups.md - rest_notifications.md - conventions_and_errors.md - authentication.md - README.md search_priority: normal status: published --- # DevPlace REST: Profiles and the Social Graph Profile data, the follow graph, the leaderboard, and avatar generation. Find users with [Search & Lookups](rest_search_and_lookups.md); follows generate entries in [Notifications](rest_notifications.md). Every endpoint follows the shared [Conventions & Errors](conventions_and_errors.md) (auth, content negotiation, pagination, status codes); see [Authentication](authentication.md) for the four ways to sign requests. ## `GET /profile` - View your own profile Render the signed-in user's own profile, in the exact format of GET /profile/{username}. Requires authentication. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `tab` | query | enum | no | Profile tab. Allowed: posts, activity, followers, following, media, awards. | ## `GET /profile/{username}` - View a profile Render a user profile, including an online-presence indicator (JSON exposes profile_online, profile_user.last_seen, xp_next_level, and xp_progress_pct). Returns an HTML page. *Minimal role:* Public **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Target username. | | `tab` | query | enum | no | Profile tab. Allowed: posts, activity, followers, following, media, awards. | > Level progress: `xp_next_level = level * 100` (total XP needed), `xp_progress_pct = xp % 100` (percentage towards next level). Both are also embedded in `profile_user`. **Sample response** ```json { "profile_user": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" }, "posts": [ { "post": { "uid": "UID", "slug": "slug", "user_uid": "UID", "title": "Title", "content": "text", "topic": "random", "stars": 0, "image": "string", "project_uid": "UID", "created_at": "2026-01-01T00:00:00+00:00", "updated_at": "2026-01-01T00:00:00+00:00" }, "maturity": "string", "author": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" }, "time_ago": "2 hours ago", "my_vote": 0, "comment_count": 0, "attachments": [ { "uid": "UID", "filename": "string", "url": "/path", "size": 0, "is_image": false, "is_video": false, "mime_type": "string", "created_at": "2026-01-01T00:00:00+00:00", "can_modify": false } ], "recent_comments": [ { "comment": { "uid": "UID", "user_uid": "UID", "content": "text", "parent_uid": "UID", "target_type": "string", "target_uid": "UID", "created_at": "2026-01-01T00:00:00+00:00" }, "author": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" }, "time_ago": "2 hours ago", "votes": { "up": 0, "down": 0 }, "my_vote": 0, "children": [], "attachments": [ { "uid": "UID", "filename": "string", "url": "/path", "size": 0, "is_image": false, "is_video": false, "mime_type": "string", "created_at": "2026-01-01T00:00:00+00:00", "can_modify": false } ], "reactions": { "counts": {}, "mine": [] } } ], "reactions": { "counts": {}, "mine": [] }, "bookmarked": false, "poll": { "uid": "UID", "question": "string", "options": [ { "uid": "UID", "label": "string", "count": 0, "votes": 0, "pct": 0 } ], "total": 0, "my_choice": "string", "voted": "string" }, "project_link": { "uid": "UID", "name": "string", "slug": "slug", "url": "/path" } } ], "badges": [ { "name": "string", "icon": "string", "description": "text", "created_at": "2026-01-01T00:00:00+00:00" } ], "achievements": [ {} ], "badge_total": 0, "badge_earned": 0, "projects": [ { "uid": "UID", "slug": "slug", "user_uid": "UID", "title": "Title", "description": "text", "project_type": "string", "status": "published", "platforms": null, "stars": 0, "is_private": false, "read_only": false, "release_date": "string", "demo_date": "string", "created_at": "2026-01-01T00:00:00+00:00", "updated_at": "2026-01-01T00:00:00+00:00" } ], "gists": [ { "gist": { "uid": "UID", "slug": "slug", "user_uid": "UID", "title": "Title", "description": "text", "source_code": "string", "language": "python", "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "updated_at": "2026-01-01T00:00:00+00:00" }, "maturity": "string", "author": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" }, "time_ago": "2 hours ago", "my_vote": 0, "comment_count": 0, "recent_comments": [ { "comment": { "uid": "UID", "user_uid": "UID", "content": "text", "parent_uid": "UID", "target_type": "string", "target_uid": "UID", "created_at": "2026-01-01T00:00:00+00:00" }, "author": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" }, "time_ago": "2 hours ago", "votes": { "up": 0, "down": 0 }, "my_vote": 0, "children": [], "attachments": [ { "uid": "UID", "filename": "string", "url": "/path", "size": 0, "is_image": false, "is_video": false, "mime_type": "string", "created_at": "2026-01-01T00:00:00+00:00", "can_modify": false } ], "reactions": { "counts": {}, "mine": [] } } ] } ], "current_tab": "string", "posts_count": 0, "is_following": false, "is_blocked": false, "is_muted": false, "is_owner": false, "profile_online": false, "can_view_api_key": false, "api_key": "string", "ai_correction_enabled": false, "ai_correction_sync": false, "ai_correction_prompt": "string", "ai_modifier_enabled": false, "ai_modifier_sync": false, "ai_modifier_prompt": "string", "interactions_enabled": false, "interactions_source": "string", "interactions_default": false, "interactions_override": false, "telegram_paired": false, "notif_telegram_paired": false, "can_manage_customization": false, "cust_disable_global": false, "cust_disable_pagetype": false, "ai_quota": {}, "correction_usage": {}, "modifier_usage": {}, "activities": [ null ], "rank": 0, "heatmap": null, "heatmap_months": null, "streak": null, "people": [ null ], "follow_pagination": null, "followers_count": 0, "following_count": 0, "viewer_is_admin": false, "xp_next_level": 0, "xp_progress_pct": 0, "media": [ { "uid": "UID", "original_filename": "string", "file_size": 0, "mime_type": "string", "url": "/path", "thumbnail_url": "/path", "has_thumbnail": false, "is_image": false, "is_video": false, "target_type": "string", "target_uid": "UID", "target_url": "/path", "created_at": "2026-01-01T00:00:00+00:00" } ], "media_pagination": null, "notification_prefs": [ null ], "awards": [ { "uid": "UID", "slug": "slug", "description": "text", "giver_uid": "UID", "receiver_uid": "UID", "generated_at": "string", "created_at": "2026-01-01T00:00:00+00:00", "image_url": "/path", "thumb_url": "/path", "giver": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" } } ], "awards_pagination": null, "awards_count": 0, "prominent_award": { "uid": "UID", "slug": "slug", "description": "text", "giver_uid": "UID", "receiver_uid": "UID", "generated_at": "string", "created_at": "2026-01-01T00:00:00+00:00", "image_url": "/path", "thumb_url": "/path", "giver": { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" } }, "can_give_award": false, "consents": [ null ], "mature_opt_in": false, "age_band": "string", "terms_version": "string", "terms_accepted_at": "string", "current_terms_version": "string", "suspended_until": "string", "suspension_reason": "string", "deletion_grace_hours": 0 } ``` ## `POST /profile/update` - Update your profile Update your own bio and links. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `bio` | form | textarea | no | Bio, up to 500 characters. | | `location` | form | string | no | Location, up to 200 characters. | | `git_link` | form | string | no | Git profile URL. | | `website` | form | string | no | Personal website URL. | **Sample response** ```json { "ok": true, "redirect": "/profile/YOUR_USERNAME", "data": null } ``` ## `POST /profile/{username}/ai-correction` - Configure AI content correction Enable or disable automatic AI rewriting of your prose and set the correction instruction. Opt-in, default off. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `enabled` | form | boolean | no | true to enable background AI correction, false to disable it. | | `sync` | form | boolean | no | true to apply the correction synchronously (the save waits), false for background. | | `prompt` | form | textarea | no | Correction instruction, up to 20000 characters. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test", "data": { "url": "/profile/bob_test", "enabled": true, "sync": false, "prompt": "Leave literary as is, only do punctuation and casing" } } ``` ## `POST /profile/{username}/interactions` - Configure Devii interactive widgets Enable or disable CA-IWP interactive prompts (ui_prompt) for this account, or reset to the administrator default. Guests always use the site default. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `enabled` | form | boolean | no | true to enable interactive widgets, false to disable. Ignored when reset is true. | | `reset` | form | boolean | no | true to clear the user override and inherit the administrator default. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test", "data": { "url": "/profile/bob_test", "enabled": true, "source": "user", "default": true, "override": true } } ``` ## `POST /profile/{username}/ai-modifier` - Configure the AI modifier Enable or disable the inline '@ai ' modifier on your prose and set its prompt. Enabled by default, applied synchronously by default. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `enabled` | form | boolean | no | true to enable the AI modifier, false to disable it. | | `sync` | form | boolean | no | true to apply the modification synchronously (the save waits), false for background. | | `prompt` | form | textarea | no | Modifier instruction, up to 20000 characters. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test", "data": { "url": "/profile/bob_test", "enabled": true, "sync": true, "prompt": "Execute what is behind `@ai` (the prompt) and replace that part including `@ai`" } } ``` ## `POST /profile/{username}/telegram` - Pair or unpair Telegram Request a single-use Telegram pairing code, or unpair the connected account. Send action=request (default) to receive a code valid for a few minutes, or action=unpair to disconnect. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `action` | form | string | no | Either request to issue a pairing code, or unpair to disconnect Telegram. | **Sample response** ```json { "ok": true, "paired": false, "code": "1234", "expires_at": "2026-06-18T12:05:00+00:00", "ttl_minutes": 5 } ``` ## `POST /profile/regenerate-api-key` - Regenerate your API key Issue a new API key and invalidate the current one. *Minimal role:* Member > > Running this invalidates the key these documentation panels use. Do it from your [profile page](https://devplace.net/profile/retoor) instead, then reload these docs. **Sample response** ```json { "api_key": "NEW_UUID" } ``` ## `POST /profile/{username}/award` - Give a member an award Create a pending award on another member's profile and enqueue image generation. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Receiver username. | | `description` | json | string | yes | Award message (1-125 characters). | **Sample response** ```json { "ok": true, "data": { "award_uid": "AWARD_UID", "award_slug": "abc123-great-work" } } ``` ## `POST /profile/{username}/regenerate-avatar` - Regenerate a user avatar Replace the user's avatar with a freshly generated random one. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Allowed for the owner or any admin. | > > Irreversible: the previous avatar is gone for good and cannot be brought back. **Sample response** ```json { "ok": true, "data": { "url": "/profile/retoor", "avatar_seed": "NEW_UUID", "avatar_url": "/avatar/multiavatar/NEW_UUID?size=80" } } ``` ## `POST /profile/{username}/customization/global` - Toggle site-wide customizations Show or suppress your site-wide custom CSS and JS without deleting it. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `value` | form | boolean | no | 1 to show your site-wide customizations, 0 to suppress them. | **Sample response** ```json { "ok": true, "redirect": "/profile/YOUR_USERNAME", "data": { "url": "/profile/YOUR_USERNAME", "cust_disable_global": true } } ``` ## `POST /profile/{username}/customization/pagetype` - Toggle per-page customizations Show or suppress your per-page custom CSS and JS without deleting it. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `value` | form | boolean | no | 1 to show your per-page customizations, 0 to suppress them. | **Sample response** ```json { "ok": true, "redirect": "/profile/YOUR_USERNAME", "data": { "url": "/profile/YOUR_USERNAME", "cust_disable_pagetype": true } } ``` ## `POST /profile/{username}/notifications` - Toggle a notification preference Enable or disable one notification type on one channel (in-app or push). Admins may target any user. Types: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | | `notification_type` | form | string | yes | One of: comment, reply, mention, vote, follow, message, badge, level, issue, reminder, harvest_stolen. | | `channel` | form | string | yes | One of in_app, push or telegram (telegram is off by default and requires a paired Telegram account). | | `value` | form | boolean | no | 1 to deliver this notification on this channel, 0 to suppress it. | **Sample response** ```json { "ok": true, "redirect": "/profile/retoor?tab=notifications", "data": { "notification_type": "vote", "channel": "push", "value": false } } ``` ## `POST /profile/{username}/notifications/reset` - Reset notification preferences Clear all of a user's notification overrides so every type falls back to the platform default. Admins may target any user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Profile owner. Must be yourself unless you are an admin. | **Sample response** ```json { "ok": true, "redirect": "/profile/retoor?tab=notifications" } ``` ## `POST /media/{uid}/delete` - Delete media Remove one of your uploaded media attachments. It disappears from your profile Media tab and from any post, project, gist, or other place it was attached. You can delete media you uploaded; administrators may remove any user's media. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `uid` | path | string | yes | Attachment uid, taken from the Media tab response. | **Sample response** ```json { "ok": true, "redirect": "/profile/retoor?tab=media" } ``` ## `POST /follow/{username}` - Follow a user Follow another user. Idempotent. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to follow. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test", "data": null } ``` ## `POST /follow/unfollow/{username}` - Unfollow a user Stop following a user. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to unfollow. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test", "data": null } ``` ## `POST /block/{username}` - Block a user Block a user. Their posts, comments and messages are hidden from you everywhere except their own profile, and they can no longer create notifications for you. Idempotent. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to block. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test" } ``` ## `POST /block/unblock/{username}` - Unblock a user Reverse a block. Their content becomes visible again. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to unblock. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test" } ``` ## `POST /mute/{username}` - Mute a user Mute a user so they can no longer create notifications for you. Their content stays visible. Idempotent. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to mute. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test" } ``` ## `POST /mute/unmute/{username}` - Unmute a user Reverse a mute. They can create notifications for you again. *Minimal role:* Member **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Username to unmute. | **Sample response** ```json { "ok": true, "redirect": "/profile/bob_test" } ``` ## `GET /profile/{username}/followers` - List followers List the users who follow a profile, 25 per page. Returns JSON. *Minimal role:* Public **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Target username. | | `page` | query | integer | no | Page number, 25 per page. | **Sample response** ```json { "username": "retoor", "mode": "followers", "count": 2, "page": 1, "total_pages": 1, "followers": [ { "uid": "UUID", "username": "bob_test", "bio": "Building things.", "is_following": false } ] } ``` ## `GET /profile/{username}/following` - List following List the users a profile follows, 25 per page. Returns JSON. *Minimal role:* Public **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `username` | path | string | yes | Target username. | | `page` | query | integer | no | Page number, 25 per page. | **Sample response** ```json { "username": "retoor", "mode": "following", "count": 1, "page": 1, "total_pages": 1, "following": [ { "uid": "UUID", "username": "alice_test", "bio": "", "is_following": true } ] } ``` ## `GET /leaderboard` - View the leaderboard Top contributors by stars. Returns an HTML page. *Minimal role:* Public **Sample response** ```json { "entries": [ { "uid": "UID", "username": "username", "avatar_seed": "string", "stars": 0, "level": 0, "rank": 0 } ], "user_rank": 0, "total_members": 0, "posts_today": 0, "total_projects": 0, "total_gists": 0, "top_authors": [ { "uid": "UID", "username": "username", "avatar_seed": "string", "bio": "text", "location": "string", "git_link": "string", "website": "string", "level": 0, "xp": 0, "xp_progress_pct": 0, "xp_next_level": 0, "stars": 0, "created_at": "2026-01-01T00:00:00+00:00", "last_seen": "string" } ], "featured_news": [ { "uid": "UID", "slug": "slug", "title": "Title", "description": "text", "content": "text", "url": "/path", "source_name": "string", "author": "string", "grade": 0, "ai_grade": 0, "status": "published", "image_url": "/path", "featured": 0, "has_unique_image": 0, "article_published": "string", "created_at": "2026-01-01T00:00:00+00:00", "synced_at": "2026-01-01T00:00:00+00:00" } ] } ``` ## `GET /awards/{slug_or_uid}/{size}` - Award image redirect Redirect to the stored PNG attachment for a published award. *Minimal role:* Public **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `slug_or_uid` | path | string | yes | Award slug or bare uid. | | `size` | path | enum | yes | Image size. Allowed: 512, 256, 64. | > > Pending or revoked awards return 404. > > Response includes long-lived cache headers. ## `GET /avatar/{style}/{seed}` - Generate an avatar Deterministic SVG avatar for a seed. Returns an image. *Minimal role:* Public **Parameters** | Name | In | Type | Required | Description | |------|----|------|----------|-------------| | `style` | path | enum | yes | Avatar style. Allowed: multiavatar. | | `seed` | path | string | yes | Seed string, usually a username. | | `size` | query | int | no | Pixel size. |