Communities

Joinable groups / subreddits + join lifecycle.

This is not the live API. This page documents an earlier, in-browser mock API (routes like /accounts, /messaging and /brand), kept for reference. The real, read-only API that runs today is described in The API.

Communities — joinable groups and subreddits

Communities are the catalog of Facebook groups, X lists, and subreddits you can listen in. The catalog is static; what mutates is the join relation (none → pending → accepted) plus the attached Facebook accountId and entry-question answers. All 7 routes serve the Groups page and every keyword scope picker.

Facebook groups gate on a connected Facebook account + answers to every entryQuestions; other platforms auto-accept. Pasted URLs are registered as new rows. Mock admin accept flips pending to accepted. Code: apps/web/src/lib/communities/server.ts and mock.ts.

GET /communities — List communities

Lists the full catalog plus any client-resolved rows (from pasted Facebook links or typed subreddits) materialized with current joinState (none/pending/accepted), accountId for Facebook joins, and accountLabel. Optional ?platform=facebook|x|reddit filters server-side. This is the source for the Groups page and every keyword/listings scope picker. Code: apps/web/src/lib/communities/server.ts:180

POST /communities/{id}/join — Join community

Attempts to join :id. Facebook requires accountId of a connected Facebook account and answers matching every entryQuestions — missing/blank answers or wrong platform account returns 400. Facebook transitions to pending (awaits admin accept), other platforms (reddit/x) auto-set accepted. Already pending/accepted is a no-op 200. Persists to localStorage. Code: apps/web/src/lib/communities/server.ts:186

POST /communities/join-by-url — Join community by URL

Parses a facebook.com/groups/… URL via parseFacebookGroupUrl, registers it as a new catalog row if unseen (registerBase), then runs the same Facebook join transition (account + answers). Dedupes by URL — already tracked rows return 200. Registers unknown URLs before joining so pasted links become first-class communities. Code: apps/web/src/lib/communities/server.ts:201

POST /communities/resolve — Resolve community URL

Resolves a pasted Facebook group URL into its Community without changing joinState. Registers unknown URLs as new rows so the form can read entryQuestions before the user answers. Persists the catalog even though no join occurs — it's a read-ahead for the join form. Code: apps/web/src/lib/communities/server.ts:218

POST /communities/resolve-reddit — Resolve Reddit community

Resolves a typed r/name (via parseSubredditName) into its Community and immediately marks it accepted — subreddits have no entry gate, so a resolved row is instantly usable as a keyword scope. Creates a new row via communityFromSubreddit if unseen. Code: apps/web/src/lib/communities/server.ts:232

POST /communities/{id}/accept — Accept pending join

Mock admin accept — transitions :id from pending → accepted. Simulates the group admin approving the join request in the Groups page 'Approve' action and tests. Returns 400 No join request to accept if state is none, 404 if unknown id. Code: apps/web/src/lib/communities/server.ts:248

DELETE /communities/{id} — Leave community

Leaves :id — resets its joinState to none, clears accountId and answers. The catalog row stays (so pasted links remain) but is no longer a member and cannot scope keywords/listings. Persists. Code: apps/web/src/lib/communities/server.ts:261

List communities

Lists the full catalog plus any client-resolved rows (from pasted Facebook links or typed subreddits) materialized with current joinState (none/pending/accepted), accountId for Facebook joins, and accountLabel. Optional ?platform=facebook|x|reddit filters server-side. This is the source for the Groups page and every keyword/listings scope picker. Code: apps/web/src/lib/communities/server.ts:180

GET/communities

Query Parameters

platformstring

Platform filter.

Value in: "facebook" | "x" | "reddit"

Response Body

Community list.

TypeScript Definitions

Use the response body type in TypeScript.

communitiesRequiredarray<object>
curl -X GET "http://localhost:5174/communities?platform=facebook"
fetch("http://localhost:5174/communities?platform=facebook")
package mainimport (  "fmt"  "net/http"  "io/ioutil")func main() {  url := "http://localhost:5174/communities?platform=facebook"  req, _ := http.NewRequest("GET", url, nil)    res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities?platform=facebook"response = requests.request("GET", url)print(response.text)
{
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}

Join community

Attempts to join :id. Facebook requires accountId of a connected Facebook account and answers matching every entryQuestions — missing/blank answers or wrong platform account returns 400. Facebook transitions to pending (awaits admin accept), other platforms (reddit/x) auto-set accepted. Already pending/accepted is a no-op 200. Persists to localStorage. Code: apps/web/src/lib/communities/server.ts:186

POST/communities/{id}/join

Request Body

application/jsonOptional
accountIdstring

Facebook account id (facebook only).

answersarray<string>

Answers to entryQuestions, one per question.

Path Parameters

idRequiredstring

Community id.

Response Body

Already joined.

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

communitiesRequiredarray<object>

Joined (now pending/accepted).

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

communitiesRequiredarray<object>

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X POST "http://localhost:5174/communities/string/join" \  -H "Content-Type: application/json" \  -d '{    "accountId": "string",    "answers": [      "string"    ]  }'
const body = JSON.stringify({  "accountId": "string",  "answers": [    "string"  ]})fetch("http://localhost:5174/communities/string/join", {  body})
package mainimport (  "fmt"  "net/http"  "io/ioutil"  "strings")func main() {  url := "http://localhost:5174/communities/string/join"  body := strings.NewReader(`{    "accountId": "string",    "answers": [      "string"    ]  }`)  req, _ := http.NewRequest("POST", url, body)  req.Header.Add("Content-Type", "application/json")  res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/string/join"body = {  "accountId": "string",  "answers": [    "string"  ]}response = requests.request("POST", url, json = body, headers = {  "Content-Type": "application/json"})print(response.text)
{
  "community": {
    "property1": null,
    "property2": null
  },
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "community": {
    "property1": null,
    "property2": null
  },
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "error": "string"
}
{
  "error": "string"
}

Join community by URL

Parses a facebook.com/groups/… URL via parseFacebookGroupUrl, registers it as a new catalog row if unseen (registerBase), then runs the same Facebook join transition (account + answers). Dedupes by URL — already tracked rows return 200. Registers unknown URLs before joining so pasted links become first-class communities. Code: apps/web/src/lib/communities/server.ts:201

POST/communities/join-by-url

Request Body

application/jsonRequired
urlRequiredstring

Facebook group URL.

accountIdstring
answersarray<string>

Response Body

Already tracked.

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

communitiesRequiredarray<object>

Joined.

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

communitiesRequiredarray<object>

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X POST "http://localhost:5174/communities/join-by-url" \  -H "Content-Type: application/json" \  -d '{    "url": "string",    "accountId": "string",    "answers": [      "string"    ]  }'
const body = JSON.stringify({  "url": "string",  "accountId": "string",  "answers": [    "string"  ]})fetch("http://localhost:5174/communities/join-by-url", {  body})
package mainimport (  "fmt"  "net/http"  "io/ioutil"  "strings")func main() {  url := "http://localhost:5174/communities/join-by-url"  body := strings.NewReader(`{    "url": "string",    "accountId": "string",    "answers": [      "string"    ]  }`)  req, _ := http.NewRequest("POST", url, body)  req.Header.Add("Content-Type", "application/json")  res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/join-by-url"body = {  "url": "string",  "accountId": "string",  "answers": [    "string"  ]}response = requests.request("POST", url, json = body, headers = {  "Content-Type": "application/json"})print(response.text)
{
  "community": {
    "property1": null,
    "property2": null
  },
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "community": {
    "property1": null,
    "property2": null
  },
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "error": "string"
}

Resolve community URL

Resolves a pasted Facebook group URL into its Community without changing joinState. Registers unknown URLs as new rows so the form can read entryQuestions before the user answers. Persists the catalog even though no join occurs — it's a read-ahead for the join form. Code: apps/web/src/lib/communities/server.ts:218

POST/communities/resolve

Request Body

application/jsonRequired
urlRequiredstring

Facebook group URL.

Response Body

Resolved.

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X POST "http://localhost:5174/communities/resolve" \  -H "Content-Type: application/json" \  -d '{    "url": "string"  }'
const body = JSON.stringify({  "url": "string"})fetch("http://localhost:5174/communities/resolve", {  body})
package mainimport (  "fmt"  "net/http"  "io/ioutil"  "strings")func main() {  url := "http://localhost:5174/communities/resolve"  body := strings.NewReader(`{    "url": "string"  }`)  req, _ := http.NewRequest("POST", url, body)  req.Header.Add("Content-Type", "application/json")  res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/resolve"body = {  "url": "string"}response = requests.request("POST", url, json = body, headers = {  "Content-Type": "application/json"})print(response.text)
{
  "community": {
    "property1": null,
    "property2": null
  }
}
{
  "error": "string"
}

Resolve Reddit community

Resolves a typed r/name (via parseSubredditName) into its Community and immediately marks it accepted — subreddits have no entry gate, so a resolved row is instantly usable as a keyword scope. Creates a new row via communityFromSubreddit if unseen. Code: apps/web/src/lib/communities/server.ts:232

POST/communities/resolve-reddit

Request Body

application/jsonRequired
nameRequiredstring

Subreddit name, e.g. r/test or test.

Response Body

Resolved + joined (accepted).

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X POST "http://localhost:5174/communities/resolve-reddit" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
const body = JSON.stringify({  "name": "string"})fetch("http://localhost:5174/communities/resolve-reddit", {  body})
package mainimport (  "fmt"  "net/http"  "io/ioutil"  "strings")func main() {  url := "http://localhost:5174/communities/resolve-reddit"  body := strings.NewReader(`{    "name": "string"  }`)  req, _ := http.NewRequest("POST", url, body)  req.Header.Add("Content-Type", "application/json")  res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/resolve-reddit"body = {  "name": "string"}response = requests.request("POST", url, json = body, headers = {  "Content-Type": "application/json"})print(response.text)
{
  "community": {
    "property1": null,
    "property2": null
  }
}
{
  "error": "string"
}

Accept pending join

Mock admin accept — transitions :id from pending → accepted. Simulates the group admin approving the join request in the Groups page 'Approve' action and tests. Returns 400 No join request to accept if state is none, 404 if unknown id. Code: apps/web/src/lib/communities/server.ts:248

POST/communities/{id}/accept

Path Parameters

idRequiredstring

Community id.

Response Body

Accepted.

TypeScript Definitions

Use the response body type in TypeScript.

communityRequiredobject

Community — joinable group / subreddit.

communitiesRequiredarray<object>

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X POST "http://localhost:5174/communities/string/accept"
fetch("http://localhost:5174/communities/string/accept")
package mainimport (  "fmt"  "net/http"  "io/ioutil")func main() {  url := "http://localhost:5174/communities/string/accept"  req, _ := http.NewRequest("POST", url, nil)    res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/string/accept"response = requests.request("POST", url)print(response.text)
{
  "community": {
    "property1": null,
    "property2": null
  },
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "error": "string"
}
{
  "error": "string"
}

Leave community

Leaves :id — resets its joinState to none, clears accountId and answers. The catalog row stays (so pasted links remain) but is no longer a member and cannot scope keywords/listings. Persists. Code: apps/web/src/lib/communities/server.ts:261

DELETE/communities/{id}

Path Parameters

idRequiredstring

Community id.

Response Body

Remaining communities.

TypeScript Definitions

Use the response body type in TypeScript.

communitiesRequiredarray<object>

Failure envelope returned with 4xx statuses.

TypeScript Definitions

Use the response body type in TypeScript.

errorRequiredstring

Human-readable failure reason, e.g. "A key needs a name".

curl -X DELETE "http://localhost:5174/communities/string"
fetch("http://localhost:5174/communities/string")
package mainimport (  "fmt"  "net/http"  "io/ioutil")func main() {  url := "http://localhost:5174/communities/string"  req, _ := http.NewRequest("DELETE", url, nil)    res, _ := http.DefaultClient.Do(req)  defer res.Body.Close()  body, _ := ioutil.ReadAll(res.Body)  fmt.Println(res)  fmt.Println(string(body))}
import requestsurl = "http://localhost:5174/communities/string"response = requests.request("DELETE", url)print(response.text)
{
  "communities": [
    {
      "property1": null,
      "property2": null
    }
  ]
}
{
  "error": "string"
}