fplaces Frontend Integration Guide
Backend Engineer: Sunday Ajayi
This guide breaks down the division of responsibilities between the stateless REST API (used for fetching static data and initiating actions) and the WebSocket layer (used exclusively for receiving real-time live updates).
All REST endpoints require the Authorization: Bearer <access_token> header, and all WebSockets require the token to be passed as a query parameter ?token=<access_token>.
PART 1: Fan / User Application
This section covers the standard mobile or web application used by venue visitors.
1. Setup, Authentication, & Onboarding (REST)
The authentication flow uses SimpleJWT. The backend is stateless, so your client must store the access and refresh tokens securely.
- Register:
POST /api/users/register/(Provideemailandpassword). User is created butis_email_verifiedisFalse. - Verify Email:
POST /api/users/verify-email/(Provideemailand the 6-digitotpsent to their email). - List Interests:
GET /api/forum/interests/(Public, no auth required). Returns the curated catalog of interests (id, name, slug, order) to render as picker tags — no longer hardcoded on the frontend. - Profile Setup:
PATCH /api/users/me/(Providepseudo_nameand optionallyinterests, an array of interest names picked from the catalog above). Settingpseudo_nameis required before the user can post. - Fetch Own Profile:
GET /api/users/me/. Besides the standard profile fields andinterests, the response includes:stat:{"posts_count": 86, "upvotes_count": 3200, "venues_count": 14}— drives the stat cards at the top of the profile screen.recent_posts: the user's 5 most recently created posts (full post objects, newest first) — drives the "Recent Posts" list on the profile screen.
- Standard Login:
POST /api/users/login/(Provideemailandpassword). Returns anaccess/refreshJWT pair plus the user's profile embedded underuser, so no separateGET /api/users/me/call is needed right after login. - Google Login:
POST /api/users/login/google/(Provide Google OAuthid_token). Automatically registers/logs in the user and returns the standard JWT tokens. - Refresh Token:
POST /api/users/token/refresh/(Providerefresh). Returns a newaccesstoken once the old one expires (after 30 minutes). - Logout:
POST /api/users/logout/(Providerefresh). Blacklists the refresh token; discard theaccesstoken locally too since it stays valid until it naturally expires. - Forgot Password:
POST /api/users/password-reset/(Provideemail). Emails auid/tokenreset link. Always returns a generic200regardless of whether the email exists, to avoid leaking account existence. - Reset Password:
POST /api/users/password-reset-confirm/(Provideuid,token, andnew_passwordfrom the reset link). - Change Password:
POST /api/users/change-password/(Provideold_passwordandnew_password). For a logged-in user changing their password without the email round-trip. - Guest Access:
POST /api/users/guest/(Provideip_addressanddevice_fingerprint). Returns a short-lived guest JWT, trial expiration info (includinghas_2d_trial_expired), and MappedIn keys. The guest session trial expires at the end of the day. - Update Guest Preferences:
PATCH /api/users/guest/update/(Providehas_tried_ar_vieworhas_tried_2d_view). Updates guest tracking flags. Requires the guest JWT in theAuthorizationheader.
2. Venue Selection & Initial Feed Load (REST)
When a user opens the app and selects a venue, fetch the historical state of that venue.
- List Venues:
GET /api/forum/venues/(Fetch available stadiums/arenas). - List Sections:
GET /api/forum/sections/?venue=<venue_id> - List Categories:
GET /api/forum/categories/(Category objects include anicon_urlfield). - Fetch Initial Feed:
GET /api/forum/posts/?venue=<venue_id>(Paginated historical posts). - Get Map Token:
GET /api/maps/token/(Authenticated). Returns a short-lived MappedIn token used to render the venue's interactive 2D/AR map. Guests get an equivalent token bundled directly into theirPOST /api/users/guest/response instead of calling this separately.
3. The Live Feed & Heatmap (WebSocket)
Immediately after fetching the initial feed, establish a WebSocket connection to the venue's room:
ws://<domain>/ws/venues/<venue_id>/?token=<access_token>
You never send data up this socket. It is purely for receiving live JSON broadcasts:
new_post:- Payload: The full JSON object of the newly created post.
- Action: Unshift/prepend it to your feed state.
upvote_update:- Payload:
{"post_id": 123, "upvotes_count": 5, "upvoted": true} - Action: Find post
123in your state and update its count.
- Payload:
post_hidden:- Payload:
{"post_id": 123} - Action: Immediately remove post
123from the UI.
- Payload:
section_heat_update:- Payload:
{"heatmap": [{"section_id": 4, "post_count": 8, "priority": "high"}, ...]} - Action: Overwrite your entire UI venue map state with this array. Map the
prioritystring ("high","medium","low") directly to your CSS glowing colors. Any missing section ID has 0 posts and is neutral.
- Payload:
presence_update:- Payload:
{"online_count": 42} - Action: Dynamically update the "live users" counter shown on the venue map screen. This tracks unique users actively connected to the venue room.
- Payload:
4. Comments (WebSocket & REST)
Each post has its own flat (1-level) comment thread. Unlike section conversations, comments are broadcast over the same venue room used for the live feed (Part 1, Section 3) rather than a socket of their own.
- Fetch Comments:
GET /api/forum/comments/?post=<post_id>(Paginated, oldest first). - Post a Comment:
POST /api/forum/comments/(Providepostandcontent, max 500 chars). Triggersnew_commenton the post's venue WebSocket room, and creates acommentnotification for the post's author (delivered over the personal notifications socket in Section 6) unless they're commenting on their own post. - WebSocket Event (
new_comment): Received on the venue room, not a separate socket.- Payload: The full JSON object of the newly created comment.
- Action: Append it to that post's comment thread if it's currently open.
5. User Interactions (REST)
When the user does something, fire a REST request. If successful, the backend automatically broadcasts the result over the WebSocket.
- Create Post:
POST /api/forum/posts/(Providecontentmax 140 chars,venue,category, and optionalsection). Triggersnew_postandsection_heat_updateon the socket. - Toggle Upvote:
POST /api/forum/posts/<post_id>/upvote/. This is idempotent. Triggersupvote_updateon the socket. - Flag Post:
POST /api/forum/posts/<post_id>/flag/(Provide optionalreason). If it hits 3 unique flags, it auto-hides and Triggerspost_hiddenandsection_heat_updateon the socket.
6. Personal Notifications (WebSocket & REST)
Users have a personal WebSocket for cross-venue, account-level alerts.
- Connect:
ws://<domain>/ws/notifications/?token=<access_token> - WebSocket Event (
new_notification): Pushes a notification object in real-time so you can increment the unread badge count. - REST Fetch:
GET /api/notifications/(List paginated notification history, newest first). - REST Unread Count:
GET /api/notifications/unread_count/(Lightweight{"unread_count": 3}for badge rendering without paginating the full list). - REST Mark Read:
POST /api/notifications/<id>/mark_read/orPOST /api/notifications/mark_all_read/.
7. Location (Map Pin) Conversations (WebSocket & REST)
Map-area chat is keyed by (venue, location_id), where location_id is the Mappedin location/place id the fan tapped on the map — not an admin Section PK (see Part 1, Section 2 for those). The client never creates a conversation id and never needs an admin to pre-seed anything for a location: the first message for a never-seen location_id auto-creates its channel.
- Fetch History:
GET /api/forum/conversations/?venue=<venue_id>&location_id=<location_id>(Both params required. Paginated, newest first. Returns an empty list with200, never a404, if nobody has posted at that location yet — a channel is only created byPOST.) - Post a Message:
POST /api/forum/conversations/(Providevenue,location_id,content(max 500 chars), and optionallylocation_name). Finds-or-creates the(venue, location_id)channel, then stores the message.location_nameonly sets the channel's display name on first create (or backfills it if still blank) — it's ignored otherwise. Triggersnew_location_messageon that location's socket. - Connect:
ws://<domain>/ws/venues/<venue_id>/locations/<location_id>/?token=<access_token>(URL-encodelocation_id). Subscribe only once the user actually opens that location's chat — don't pre-subscribe to every pin on the map. - WebSocket Event:
{"type": "new_location_message", "message": { ...same shape as the POST response... }}- Action: Append
messageto that location's conversation state.
- Action: Append
- WebSocket Event (
presence_update):- Payload:
{"online_count": 5} - Action: Update the "live users" indicator showing how many unique users are actively viewing this location's conversation.
- Payload:
Admin Sections (North Stand / South Stand / VIP) are unaffected and still used for onboarding, the venue post feed's optional section, and the heatmap — they are a separate, much smaller catalog and are never auto-created from map pins.
PART 2: Admin / Moderator Dashboard
This section covers the web-based administrative panel used by staff. Accessing these endpoints requires an account with is_staff=True or user_type=admin.
1. Dashboard Authentication (REST)
Admins log in using the exact same login endpoints as users (POST /api/users/login/). Ensure your dashboard checks if the decoded JWT belongs to an admin before allowing entry.
2. Platform Statistics Overview (REST)
Used to render the charts and metric cards on the admin homepage.
- Fetch Dashboard Stats:
GET /api/admin/stats/- Returns: Aggregated metrics including total registrations, verified users count, total posts/comments, and categorical breakdowns of activity.
3. Content Moderation (REST)
Admins have elevated visibility into all content, including posts hidden from the public.
- View Moderation Queue:
GET /api/admin/posts/flagged/- Fetches posts sorted descending by
flags_count. This is your primary moderation queue.
- Fetches posts sorted descending by
- Hide Post:
POST /api/admin/posts/<post_id>/hide/- Removes the post from the public feed immediately. Triggers the
post_hiddenandsection_heat_updateWebSocket events for active fans.
- Removes the post from the public feed immediately. Triggers the
- Show/Unhide Post:
POST /api/admin/posts/<post_id>/show/- Reverses a manual or auto-hide.
- Clear Flags:
POST /api/admin/posts/<post_id>/clear-flags/- Resets a post's
flags_countto0and archives the flag tickets after a moderator has reviewed it and deemed it safe.
- Resets a post's
4. User Management (REST)
Complete control over user accounts.
- List Users:
GET /api/admin/users/(Fetch all users, paginated, including archived/unverified accounts). - Archive User:
DELETE /api/admin/users/<user_id>/orPOST /api/admin/users/<user_id>/archive/(equivalent). Soft-deletes the user (is_archived=True), disabling their login without touching their posts/comments. - Restore User:
POST /api/admin/users/<user_id>/restore/ - Toggle Staff Status:
POST /api/admin/users/<user_id>/toggle-staff/(Flipsis_staffto its opposite value; call again to toggle back).
5. Broadcast Notifications (REST)
Send a targeted push and/or email blast to users matched by venue, section, category, or explicit user ids (a union of whichever filters are provided — at least one is required).
- Send Broadcast:
POST /api/admin/notifications/(Providesubject,message,channels(["email", "push"]), and at least one ofvenue,section,category, orusers).- Push channel creates a
broadcast-verbNotificationper matched user, delivered live over theirws/notifications/socket. - Returns:
{"users_targeted": 50, "email_sent": 48, "push_sent": 50}. Delivery is best-effort per user/channel — one failed send doesn't stop the rest of the broadcast.
- Push channel creates a