This app is read-only. It displays builders, projects, and teams that are
created and managed in the main Boundless app. Every read comes from the shared
boundless-nestjs backend, so this app and boundless-platform stay consistent
(a project or user followed in the platform is read here from the same data).
The public read API was implemented by the backend team. This document mirrors
boundless-nestjs/docs/builders-public-reads.md and maps each endpoint to the
screen that uses it. If the two ever drift, the backend doc is the source of
truth.
- This app calls
GETendpoints only. All writes (follow, create, edit, upload) stay in boundless-platform. - Follows and profiles are stored once and shared. We reuse the same reads, we do not duplicate the data model.
- Prefix: every path is served under the global
/apiprefix.GET /projectsisGET /api/projects. Base URL comes fromNEXT_PUBLIC_API_URL(staging:https://stage-api.boundlessfi.xyz). - Auth: the backend runs a global session guard. These routes opt out with
@AllowAnonymous(), so they are public. Personalize-if-present routes use@OptionalAuth(). - No PII: public reads never return
emailor other sensitive account fields. Identity and user-authored public profile fields only. - Pagination: list responses return
{ data, pagination: { page, limit, total, totalPages, hasNext, hasPrev } }.
- Public: implemented and public. Use directly.
- Private-aware: public, but returns
404when the target profile is private.
| Endpoint | Purpose | Status |
|---|---|---|
GET /users/top-builders |
Top builders row. Card-ready. Accepts limit. |
Public |
GET /projects/featured |
Featured projects row | Public |
GET /discover/landing |
Ecosystem feed / counts | Public |
GET /discover/recent-winners |
Recent winners highlight | Public |
GET /users/directory?limit=N |
Fallback source for a builders strip | Public |
GET /leaderboard |
Ranking + reputation (tier, score, stats). Not card-ready. | Public |
Top builders: use
GET /users/top-builders. It returns a flat array of card-ready builders in the standard{ success, message, data, meta }envelope, with everythingBuilderCardViewneeds:{ "id", "name", "username", "image", "role", "location", "country", "skills": [], "followers": 0, "projects": 0 }Map:
name-> displayName,image-> avatarSrc,location-> location,skills/followers/projectsstraight across. Do not maprole(it is the account role, e.g."user", not a professional title).GET /leaderboardis kept for ranking/reputation use only; it does not carry profile fields (role, location, followers, projects), which is why the landing top-builders row moved toGET /users/top-builders.A stats strip can derive counts from the
pagination.totalofGET /users/directory,GET /projects, andGET /organizations.
| Endpoint | Purpose | Status |
|---|---|---|
GET /users/directory |
Public builders directory. Filters: country, skills (repeatable/CSV), status, search, sort (name_asc|name_desc|newest|oldest), page, limit. Public profiles only. |
Public |
GET /users/filters |
Facets: skills[], countries[] (with counts), statuses[] |
Public |
The directory is
GET /users/directory, notGET /users.GET /usersis Admin-only and is not used by this app.
| Endpoint | Purpose | Status |
|---|---|---|
GET /users/:username |
Public profile. Embeds orgs, projects, stats, followStats. | Public |
GET /users/:username/stats |
Public profile stats + follow counts | Private-aware |
GET /users/:username/activity |
Contribution timeline (limit, offset) |
Private-aware |
GET /users/:username/organizations |
Teams the builder belongs to, with role | Private-aware |
GET /users/:username/followers · /following |
Follower / following lists | Public |
GET /projects?creatorId=:userId |
The builder's projects | Public |
| Endpoint | Purpose | Status |
|---|---|---|
GET /projects |
Directory. Filters: creatorId, organizationId, tags (repeatable/CSV), category, publicStatus, originType, featured, search. Never returns IDEA drafts. |
Public |
GET /projects/search |
Search public projects | Public |
GET /projects/filters |
Facets: categories[] + tags[] (with counts), publicStatuses[], originTypes[] |
Public |
| Endpoint | Purpose | Status |
|---|---|---|
GET /projects/:slug |
Project header and body (PII-scrubbed) | Public |
GET /projects/:slug/members |
Contributor roster (from ProjectMember). Creator always included as owner. Drafts 404. |
Public |
GET /follows/entity/project/:id/followers |
Project follower list. The generic entity/:entityType/:entityId/followers route stays authenticated. |
Public |
GET /bounties?projectId= · GET /grants?projectId= |
Related opportunities | Public |
| Endpoint | Purpose | Status |
|---|---|---|
GET /organizations |
Public directory. search, sortBy (createdAt|name), sortOrder, page, limit. Counts only, no member PII. |
Public |
GET /organizations/search |
Search organizations | Public |
| Endpoint | Purpose | Status |
|---|---|---|
GET /organizations/profile/:idOrSlug |
Team profile | Public |
GET /organizations/profile/:idOrSlug/funded |
Funded / supported projects | Public |
GET /organizations/:id/members |
Public roster (identity fields only, capped, with total) |
Public |
GET /organizations/:id/stats |
Projects / hackathons / bounties / grants / participants / prize pool + followers | Public |
| Endpoint | Purpose | Status |
|---|---|---|
GET /leaderboard |
Leaderboard widget | Public |
GET /follows/:entityType/:entityId/check |
"Am I following" state (only if this app has logged-in sessions) | Auth |
POST / DELETE /follows/:entityType/:entityId and every other write stay in
boundless-platform. This app never calls them.
From migration 20260725000000_builders_public_reads:
BuilderStatusenum (AVAILABLE,OPEN_TO_WORK,BUSY,UNAVAILABLE) and indexedUserProfile.country/UserProfile.statusback the directory filters and facets.ProjectMembermodel (projectId,userId,role,joinedAt) is the first-class replacement for the untypedProject.teamMembersJSON and backsGET /projects/:slug/members.
Follows and profiles are stored once and read by both apps. A user or project followed in boundless-platform is read here through the same endpoints. Nothing is duplicated.