docs
/
Full courses — Appmint

Open a member community and handle its first moderation report

Zara’s design question, original joinery sketch, comment count and insightful reaction in Studio

The result: a conversation attached to the right community page, with enough context for the owner to understand and moderate it.

Who this is for: business owners, community managers and developers connecting a member app. Time: 35 minutes for Studio setup and moderation; allow another 45 minutes for the developer lab. Level: beginner administration, then PRO integration. Product: Appmint Studio Manager → Community and AppEngine’s community APIs. Build checked: Studio 0.6.2; public workflow rehearsed 21 September 2026; privacy and hashtag acceptance 24 September 2026. Example business: Cedar & Form, an interior-design studio.

What you will have at the end

You will create Tutorial Cedar & Form Studio Circle, define what belongs there, inspect a member conversation, and handle an unrelated promotional post without losing the useful discussion. You will understand the difference between a community page, a connection, a direct message and a moderation report.

The developer lab supplies the actual requests for joining, posting an image, commenting, reacting, connecting, arranging a meeting and reporting a member. Its results appear in the same Studio screens used in the beginner lessons.

Choose your starting point. If you manage an existing member app, work through Parts 1–3 with your training members. If you are creating the member experience, complete Part 1, then the PRO lab in Part 4, then return to Parts 2–3. Creating a page in Studio creates its community record; it does not automatically add a member-facing website or a navigation link to your site.

What you need

  • Your organization and Studio sign-in from Welcome to Appmint.
  • Access to Community. For staff access, use roles and groups.
  • A training community containing fictional information. The policy limitations beside the setup steps matter before you use real project details.
  • For member participation: a connected member app, or a developer completing Part 4. Customer accounts and staff accounts have different purposes; clients do not need Studio access to participate.
  • For the image exercise: download the joinery sketch. It is an original illustration for this lesson, not a construction specification.

The screenshots use an organization created for these tutorials. Your organization name and record IDs will differ. Keep your own IDs when following the API examples.

The story

Cedar & Form’s clients often ask similar questions about materials and suppliers. Zara wants advice before attending a joinery session. Kofi has a recommendation. Their discussion should be easy for another client to find next week.

The studio also needs a calm way to handle irrelevant promotion. Sam is a fictional training member who posts one deliberately unrelated message. We will review that report, remove the message from the member feed, and practice restoring it if the moderator makes a mistake.

The useful outcome is a place where a good answer stays available and a poor contribution can be handled deliberately.

The route

flowchart LR
    P[Create Studio Circle] --> M[Members join]
    M --> C[Question + sketch + reply]
    C --> F[Owner inspects Feed]
    R[Member reports a person] --> Q[Review in Moderation]
    Q --> A[Change post status in Feed]
    A --> V[Check the member result]

Page means a community space here. It is different from a website page built in Build Studio. Groups is another separate tab: it manages group chats. Choosing the community page type Group does not itself create a group chat.

Part 1 — Give the community a clear purpose

1. Open Community and find the working tabs

Choose Community in Studio’s sidebar. If its submenu opens, choose Dashboard. The top strip contains Dashboard, Pages, Feed, Stories, Connections, Follows, Messages, Groups, Meetings, Announcements, Badges, Moderation, Hashtags and Notifications. On narrower screens, move along the strip to reach the later tabs.

The dashboard summarizes activity. Pages manages the spaces; Feed manages posts; Moderation manages member reports. You will move between these three rather than trying to do everything from the dashboard counters.

Community dashboard before the first page

1 — Pages starts the setup. 2 — Moderation is the report queue you will use later.

You should see: a dashboard appropriate to your organization. An empty training organization shows zero pages and posts. Existing organizations may already have event pages.

2. Add the page’s identity

Open Pages → Add Page. In the General section, enter:

FieldExampleWhy it matters
Title *Tutorial Cedar & Form Studio CircleThe name members and moderators recognize.
Slugtutorial-studio-circleA short identifier without spaces. It is not, by itself, a finished website URL.
TypeGroupDescribes a shared interest space.
Short DescriptionShare design questions, useful finishes and session recommendations. Keep client addresses and private project files out of posts.Gives newcomers a useful boundary in two sentences.
CategoryInterior designDescribes the subject.
Tagsdesign, materials, studio-circleAdds useful organizing terms.
StatusActiveKeeps this page in the active set.

The longer Description is a rich-text area. Use it for community guidance when you need more than the short description: what to ask, how to make a question useful and where private project discussions belong. You can leave it empty for this first exercise.

Community page identity fields

1 — A recognizable title. 2 — A separate, URL-friendly slug. The screenshot shows the actual creation drawer.

You should see: your values still in the drawer. The page is not saved yet.

3. Choose the settings deliberately

Collapse General and Media if necessary to bring Settings into view. Choose:

ControlExercise valueMeaning to communicate to members
VisibilityPublicUse this space for information suitable for public discussion.
Join PolicyOpenMembers are intended to join without an approval round.
Posting PolicyMembersJoin before contributing.
Moderation LevelFlagged OnlyThe community’s intended review process focuses on flagged contributions.

Leave Feed, Stories, Announcements and Media Gallery checked and Chat unchecked for this exercise. These feature settings do not establish that your member app renders every feature; its interface must support them.

Page settings with public visibility, open joining and flagged-only moderation

1 — Visibility. 2 — Join policy. 3 — Moderation level.

Policies now have verified effects on the member endpoints. Private pages are hidden from anonymous, outside and pending members. Approval Required saves a pending membership; Admins Only refuses an ordinary member's post; Pre-Approve saves that member's post as pending. The author can see their pending submission, while other member-facing reads do not expose it. Staff moderation is a separate administrative view. These were checked with separate identities after saving the settings in Studio; keep the same boundary tests when connecting your own member app.

Actual saved private, approval-required and pre-approval settings.

The tested page-post endpoint does require page membership. That check is useful, but it does not make the other selectors effective access controls.

4. Save, reload and read the row back

Choose Create at the bottom of the drawer. Wait for the page to appear, then reload Studio, open Community → Pages, and find its title again.

Saved community page after reload

1 — The persisted page. 2 — Edit opens its settings again.

You should see: group, public, active, and initially 0 members. Studio’s page creation did not automatically add the signed-in owner as a community member.

Use the pencil Edit action to revise the description later. View JSON shows the record, including its sk ID for a developer. Do not copy the page’s slug into an API field expecting that record ID.

Try it. Explain the community in one sentence that names both its subject and its audience. “Ask other Cedar & Form clients about materials and design sessions” is more useful than “Welcome to our community.”

Check yourself. Does seeing the page in Studio mean your website now has a working Join button? No. The member app is a separate part of the setup. Use its verified community screen or Part 4’s integration lab.

Part 2 — Read a real conversation without losing its context

1. Confirm that members actually joined

Have your training members join through your connected app, or perform the join requests in Part 4. In Studio, return to Pages and choose Refresh.

Three joined members displayed on the community page

The three members in this exercise are Zara, Kofi and Sam. These are customer accounts used only for practice.

You should see: the Studio Circle member count has changed to 3. Creating a customer account alone did not produce that count; each member joined this specific page.

Joining a second page: reuse the same customer identity and join each page separately. The repaired local build supports this; creating another customer is unnecessary. If an older installation returns a duplicate membership-name error, ask its administrator to update it before continuing.

2. Find the question in Feed

Open Feed. Use the All Pages selector to choose Tutorial Cedar & Form Studio Circle. Keep All Statuses and All Types while learning the screen.

The exercise post reads:

Which session on 3D-printed joinery should I not miss? #materials

Kofi’s answer is:

The Friday 11:00 one — bring the sketch.

Zara’s post and engagement counts in the admin Feed

The row tells you the Author, Content, Type, Page, Media, Engagement and Status. Under Engagement, the heart is reactions, the speech bubble is comments and the share icon is shares. Our first conversation has one reaction, one comment and no shares.

You should see: Zara’s name, the correct community page, and the saved counts. A post made without a page ID is a global post; do not assume every post belongs to the page you happened to be viewing elsewhere.

3. Open the post to inspect the details

Select the post’s text. The Post Detail drawer shows the full question, Posted in, the attachment, engagement totals, Reaction Breakdown, hashtags and visibility.

Post Detail after the image has loaded

Our image is a discussion prompt, not decoration. It gives Kofi something specific to answer:

Original concept sketch of a timber panel and printed connector

You should see: Media (1), 1 Reactions, 1 Comments, and insightful 1. The current admin drawer shows the comment count; it does not display the full conversation thread. Read the comment through your member app or the comments request in Part 4.

Image loading: wait for the thumbnail to load before judging the attachment or capturing a tutorial. The first capture showed an empty media tile while the image was still loading; the later capture contains the actual sketch.

4. Use a poll when the answer should be a choice

The developer exercise creates a second post: Tutorial: Which material should we compare next? Its two options are Timber and Recycled composite. Open that post in Feed to inspect the result.

Poll showing one vote for Timber

You should see: one vote, with Timber at 100%. The poll allows one choice. Kofi’s second vote returned Already voted rather than adding another vote.

A poll helps choose a subject; a comment explains a reason. Use both when you need a decision and the context behind it.

Try it. Ask one specific question about the sketch, then compare it with “Any thoughts?” Which would help a new member write a useful reply?

Check yourself. Does a reaction count of one mean one new reaction for every tap? No. Repeating the same reaction toggles it off; the next tap adds it again. The API exercise verifies that behavior.

Part 3 — Review the person, then moderate the post

1. Open the pending report

Sam’s practice post says:

Tutorial moderation sample: unrelated promotion. This is a fictional practice post.

Zara reports Sam with reason spam and includes the post ID as context. Open Moderation. A pending-report notice appears above Moderation & Reports.

Pending report identifying the reporter and reported member

Read the Reporter, Reported, Reason, Details and Status columns. This report concerns a person. The offending post is separate content that you must identify before acting.

Select the row’s eye icon, View Details.

Report details with reason and post context

You should see: Zara as reporter, Sam as the reported member, the unrelated-promotion explanation and the saved context. The reporter’s display name may be blank while their email is visible; use that email to identify them.

2. Understand the review choices before selecting one

The drawer offers Dismiss, Record Warning, Record Ban Decision and Remove Content. Warning and ban choices record decisions; they do not send a warning or suspend an account. For this exercise, select Remove Content, then close the drawer, reload Studio and return to Moderation.

Report review choices

Action Taken persisted after reviewing the report

You should see: Action Taken, with content_removed beneath it.

Verify the effect: the repaired Remove Content action checks the report’s post ID and author, removes that post, then saves the review. Its member link should return 404 — Post not found and the page feed should exclude it. If the report has no valid post reference, correct the context or locate the post in Feed; the report stays open when removal fails. The screenshot above is historical; the current result is shown below.

Report review after the verified local repair

3. Find the post in Feed and mark it for review

Open Feed, choose Studio Circle in the page filter and find Sam’s exact practice text. After the repaired report action it should be removed. For this fictional rehearsal only, select Restore once so you can practice the separate Flag and Remove controls below. The older screenshot below shows the pre-repair behavior.

Post still active after Remove Content in the report drawer

1 — The reported post. 2 — The Feed action that changes the post itself.

Select the row’s flag icon, Flag. The status becomes flagged.

Flagged post in Feed

You should see: the flagged status. Members can still retrieve a flagged post. Use Flag to draw moderation attention, not to hide content.

4. Remove the post from the member feed

On that same row, select Remove, the trash-shaped icon. This action changes the post status to removed; the row offers Restore afterwards.

Removed post with the Restore action available

1 — Removed status. 2 — Restore is available if the decision was wrong.

You should see: the row remains available to the moderator under All Statuses, while the member feed no longer includes it. In the integration test, fetching its direct post endpoint returned 404 — Post not found. This is the member-visible result that the report’s review status alone did not achieve.

5. Practice undoing a mistake

Select Restore on the practice post. Its status returns to active. The member API can retrieve it again. Then select Remove once more to leave the exercise in its moderated state.

Restored practice post

Restoration is useful when a moderator selects the wrong row. It is not a reason to skip reading the author, content and page first.

6. Keep reports, blocks and content removal distinct

ActionResult observed in this exercise
Zara reports SamA pending report is created; Sam can no longer send Zara a direct message.
Remove Content in the reportThe linked post is removed before the successful review is recorded.
Flag Sam’s postPost is marked flagged and remains readable.
Remove Sam’s post in FeedMember feed excludes it; direct post request returns 404.
Restore Sam’s postMember access to the post returns.

A block or report does not hide all of that person’s posts from the feed. Content removal is a separate moderation action.

Another report: an unresolved duplicate is rejected as Report already pending. After the earlier report is reviewed, a new report can be submitted; this was verified after the local status repair. Do not repeatedly submit an unresolved report.

Try it. Tell another moderator what you checked before removing the post: the author, page, text, report reason and member-visible result.

Check yourself. The report says content_removed, but the Feed row says active. Which one determines whether members can still read the post? The post status. Complete the Feed action and verify the member result.

Part 4 — PRO: connect the member experience with AppEngine

This lab uses real endpoints exercised against the tutorial organization. It is for developers using a private API client. It does not describe buttons that are missing from your website.

Keep the API base URL in an environment variable in your API client. Use the AppEngine origin configured for your organization’s deployment. In Studio’s browser developer tools, Network shows that origin on its API requests. Do not substitute the Studio website URL for the API URL.

1. Prepare identities and authorization

For this private training lab, sign in as the organization owner with:

POST/profile/user/signin
{"email":"YOUR_OWNER_EMAIL","password":"YOUR_OWNER_PASSWORD"}

Keep the returned token in the API client’s private environment. Do not put an owner token in a member website, a mobile app, screenshots or this Markdown file. A deployed client needs the application-authentication setup covered in the connected-client course.

Register three fictional customers in the training organization using POST /profile/customer/signup, the same orgid header and JSON bodies in this shape:

{
  "firstName": "Zara",
  "lastName": "Tutorial",
  "email": "[email protected]",
  "password": "YOUR_UNIQUE_TRAINING_PASSWORD"
}

Repeat for Kofi and Sam. Use unique passwords. These .invalid addresses are deliberate training identities, not addresses for invitation delivery. Registration can invoke the organization’s welcome-notification setup; use your isolated training organization.

For an existing account, use POST /profile/customer/signin with email and password. Save each returned customer.sk and token privately. Do not sign up repeatedly when an account already exists.

For the member requests below, the private lab sends:

Content-Type: application/json
orgid: YOUR_ORGANIZATION_ID
Authorization: Bearer YOUR_PRIVATE_LAB_OWNER_TOKEN
x-client-authorization: CURRENT_MEMBER_TOKEN

The current member changes between Zara, Kofi and Sam. Keeping Zara’s token selected while testing “as Kofi” would attribute the action to the wrong person. In the practiced responses, the post author was Zara and the comment author was Kofi; inspect those identities in your own results too.

2. Join the page before posting

Obtain the Studio Circle record’s sk through Pages → View JSON. Call it PAGE_ID below. For Zara, send:

POST/client/community/pages/PAGE_ID/join

Send {} as the JSON body. Repeat with Kofi’s and Sam’s member tokens. The response contains the membership’s page, email, role and active status.

Read the memberships back with GET /client/community/pages/PAGE_ID/members. Return to Studio and refresh Pages. This is the join operation that produces the count shown in Part 2.

Before joining, Zara’s attempted page post returned 403 — You must be a member of this page to post. If you see it, check both the page ID and the member token before retrying.

3. Create the question and upload its sketch

As Zara, send POST /client/community/posts:

{
  "page": "PAGE_ID",
  "content": "Which session on 3D-printed joinery should I not miss? #materials",
  "contentType": "text"
}

Replace PAGE_ID with the actual record ID. Save the returned post sk as POST_ID. Confirm data.author, data.page and data.pageName before moving on.

Upload the PNG sketch using POST /client/community/media/upload. Change this request’s body to multipart form-data, add a file field named file, and select the PNG. Let your client set the multipart boundary rather than retaining the JSON Content-Type header.

The successful response supplies path and signedUrl. Attach those returned values with PUT /client/community/posts/POST_ID:

{
  "media": [
    {"type":"image","path":"RETURNED_PATH","url":"RETURNED_SIGNED_URL"}
  ]
}

Use the returned media values in the request, but keep signed URLs out of public logs and video captions. The course screenshot displays the image itself. Refresh Studio’s Feed and reopen the post to check the attachment after it loads.

4. Add a reply and a reaction as Kofi

Switch x-client-authorization to Kofi’s token. Send POST /client/community/posts/POST_ID/comments:

{"content":"The Friday 11:00 one — bring the sketch."}

Then send POST /client/community/react:

{"target":"POST_ID","targetType":"post","type":"insightful"}

The response is {"action":"added","type":"insightful"}. Repeat the same request once and it returns removed; repeat again to leave the reaction present.

Read GET /client/community/posts/POST_ID/comments. The stored comment includes Kofi’s author identity and the complete reply. Read GET /client/community/feed?page=PAGE_ID to confirm the page-scoped post and counts. Studio’s Feed reflects those same saved records after Refresh.

5. Connect two people before arranging their meeting

As Kofi, first attempt POST /community/meetings with:

{
  "title":"Tutorial Coffee at the atrium",
  "participantIds":["ZARA_CUSTOMER_ID"],
  "startTime":"2026-10-02T15:30:00.000Z",
  "duration":30,
  "timezone":"America/Chicago",
  "locationType":"in_person",
  "location":"Atrium"
}

Use a suitable future date for your own exercise. The example instant is 10:30 a.m. in Chicago on that date. A timestamp ending in Z is UTC; do not type a local time and append Z without converting it.

Before the connection exists, the response is 403 — You must be connected with all participants. Create it with POST /community/connections/request as Kofi:

{"targetId":"ZARA_CUSTOMER_ID","message":"Tutorial: let us compare session notes."}

Save the connection sk. Switch to Zara and send PUT /community/connections/CONNECTION_ID/respond:

{"action":"accept"}

Switch back to Kofi and repeat the meeting request. It now succeeds. In Studio, Connections shows the accepted relationship; Meetings shows the proposed meeting.

Accepted connection between the two training members

Meeting created after the connection was accepted

A created meeting is not the same as every participant having accepted it. Inspect its status and participant responses before treating it as a confirmed appointment.

6. Send a direct message and understand its boundary

As Zara, send POST /community/messages:

{
  "recipientId":"KOFI_CUSTOMER_ID",
  "content":"Tutorial: Shall we compare joinery sketches before the session?"
}

This message also succeeded before the connection existed in our exercise. Direct messages and meetings have different prerequisites.

The community message in Studio

Open Community → Messages to inspect the resulting conversation. This is separate from CRM → Chat, which handles customer-facing support, and from staff collaboration in Workspace.

7. Create the moderation exercise

As Sam, send POST /client/community/posts with Studio Circle’s page ID and the fictional promotional text from Part 3. Save its post ID.

As Zara, send POST /community/reports:

{
  "reportedId":"SAM_CUSTOMER_ID",
  "reason":"spam",
  "details":"Unrelated promotion in the Studio Circle. Tutorial moderation exercise.",
  "context":{"source":"community","postId":"SAM_POST_ID"}
}

The report targets Sam’s customer ID; the post ID is supporting context. A repeated report returns Report already pending. As Sam, a message to Zara now returns 403 — Cannot send message to this user. Zara’s feed still includes Sam’s post until the moderator removes it.

Complete Part 3 in Studio, then check:

RequestAfter FlagAfter RemoveAfter Restore
GET /client/community/posts/SAM_POST_ID200; post readable404; Post not found200; post readable
GET /client/community/feed?page=PAGE_IDIncludes Sam’s postExcludes Sam’s postIncludes Sam’s post

8. Add the single-choice poll

As Zara, send POST /client/community/posts:

{
  "page":"PAGE_ID",
  "content":"Tutorial: Which material should we compare next?",
  "contentType":"poll",
  "poll":{
    "question":"Which material should we compare next?",
    "allowMultiple":false,
    "totalVotes":0,
    "options":[
      {"id":"timber","text":"Timber","votes":0,"voters":[]},
      {"id":"composite","text":"Recycled composite","votes":0,"voters":[]}
    ]
  }
}

Save the new post’s ID. As Kofi, send POST /client/community/posts/POLL_POST_ID/vote with {"optionId":"timber"}. Repeat with composite: the single-choice poll returns Already voted. Reopen the poll in Studio to see the saved total.

Try it. Follow Zara as Kofi with POST /client/community/follow, body {"following":"ZARA_EMAIL","followingType":"user"}. Inspect Follows in Studio. A one-way follow does not replace the accepted connection required for meetings.

Check yourself. Can you explain which token selects the acting member, which ID selects the page and which ID selects the post? Keep these three decisions explicit in your client code. Stop a dependent request if the preceding response failed or returned no ID; omitting a page can accidentally create a global post.

Part 5 — Grow the community deliberately

Announcements: prepare a clear instruction

Open Announcements → New Announcement. The drawer contains Page, Title, Content, Media, Priority, Target Audience, Target Roles, Pinned, Expires At and Status.

A useful first announcement would be:

  • Title: Tutorial Bring one material question
  • Content: Bring one material question and a sketch. We will compare practical options before the session.
  • Priority: Normal
  • Target Audience: All
  • Status: Draft while preparing it.

Announcement draft and the page selector

Click Page, type Cedar, and select Tutorial Cedar & Form Studio Circle from the results. Confirm the selected title stays in the field; typing a search term alone does not select a page. Enter the title and content above, keep Normal, All and Draft, then choose Create.

Reopen the saved row with Edit. Check the page title, announcement title, content and Draft status. The repaired picker and draft readback passed locally. Leave it as a draft in this training exercise; saving is not evidence of member publication or push delivery.

Saved announcement draft attached to Studio Circle

Use the other tabs for a specific purpose

TabWhat to use it forCurrent lesson boundary
StoriesShort-lived member updates. The empty state says Stories are created by users.Source defines 24-hour expiry; creation and expiry were not timed in this walkthrough.
GroupsGroup-chat records, with New Group Chat and types such as group, page chat and session chat.This is separate from a page of type Group. A working member chat screen still needs verification.
BadgesRecognition; New Badge, categories, rarity and active/retired filters.Creating a badge definition and awarding it to a member are separate operations. No award was made here.
HashtagsInspect tags extracted from posts.New tags now display their normalized text. The local two-post check displayed policytrainingfixed with usage count 2; search the tag text to find it. Older generated-name records may remain from before the repair.
NotificationsInspect notification records, read/unread counts and push totals.The test screen showed zero despite the preceding interactions. Saved community actions are not evidence that push alerts were delivered.

Stories empty state explains where stories originate

Group Chats is separate from community Pages

Badge management and its categories

For an event community, continue with running an event. AppEngine contains hooks for creating event pages and adding ticket holders. Check the resulting page and membership. The earlier event rehearsal hit a duplicate membership-name defect. Explicit membership in a second page now passes after the local repair; automatic ticket-to-membership still needs its event rehearsal. A ticket did not guarantee membership in that second page.

For the member phone experience, use EventOxygen. The stock app serves its configured organization; creating a Studio Circle in your own organization does not redirect that stock app to your data. A branded build needs the organization and application configuration described in the event courses.

Try it. Choose one next feature based on a member need: an announcement for instructions, a poll for a choice or a discussion for explanation. Avoid adding all the surfaces before you can support the first one.

Check yourself. Does the presence of a management tab mean your phone app has the matching member screen? No. Verify the client screen and its result before advertising it to members.

The actual repaired hashtag and its two recorded uses.

If something goes wrong

SymptomFirst checkNext action
Post creation says membership is requiredActing member token and page IDJoin that page with that member, then retry once.
The page exists in Studio but nobody can find a website linkWhether a member-facing app was actually configuredConnect the member surface; a community record alone does not build one.
Second-page join returns E11000 ... data.name_1Whether the installation includes the membership-name repairKeep the same customer; ask the administrator to update the older build.
Page creation reports a membership errorWhether the page itself was already savedSearch Pages before retrying creation. Our API-created policy page remained saved even though owner membership failed.
A supposedly private page is readableActing identity, saved visibility and installed buildRetest signed out and as a nonmember; report any unexpected 200 before adding private material.
Meeting creation returns a connection errorConnection is accepted, not merely pending or followedSend and accept the connection, then create the meeting.
Report says Action Taken but content remainsPost status in FeedRemove the specific post in Feed and verify the member result.
Report still says already pending after reviewWhether another pending report exists and whether the installation includes the status repairResolve the existing pending report; avoid repeated submissions.
Reporting stops DMs but posts remainSeparate messaging and feed behaviorModerate the offending content separately.
Image tile appears blankWhether the attachment finished loadingWait, reload the post, then verify its media URL and attachment data privately.
Announcement Page field has no optionsPage exists and is savedCancel the unsaved draft and report the picker failure.
Admin Feed text overlaps adjacent columnsCurrent table layoutOpen Post Detail to read the complete text.

What happened behind the scenes

For developers: records, defects and source locations

Source paths below are relative to /Users/imzee/projects in the reviewed checkout.

  • websitemint/packages/ui/src/components/community/pages-list.tsx: real page form, generic save and member-count display. Page defaults include active status; the member API does not apply the same defaults.
  • appengine/src/community/community-page.service.ts: join requires a page/email membership lookup; creation may save a page before adding its owner. The practiced second membership collided with the collection’s unique data.name index. Public page details do not enforce visibility. Page list filters active status and does not use the supplied text query in the checked implementation.
  • appengine/src/community/community-post.service.ts: page-membership check before posting; page/global feed filtering; comments, reaction toggling, polls and soft removal. A missing page produces a public/global post. A removed post returns 404 through the member single-post method.
  • websitemint/packages/ui/src/components/community/posts-list.tsx: the Feed actions write data.status, with Restore returning it to active. The detail drawer shows counts and reaction breakdown, not the comment thread.
  • appengine/src/community/community-connection.service.ts, community-meeting.service.ts, community-messaging.service.ts: connections, meeting prerequisites and messaging block checks.
  • appengine/src/community/community-block.service.ts: reports are stored as block records with flat reported and reportStatus fields. Messaging checks those block records. The pending-report check still reads the flat status.
  • websitemint/packages/ui/src/components/community/block-list.tsx: review writes nested data.report.status and action labels. This explains both the status mismatch and why Remove Content in the report drawer did not remove the post.
  • community-client.controller.ts and community.controller.ts: the two endpoint prefixes used in the private lab. Member identity is supplied through CurrentCustomerOrUser.
  • websitemint/packages/ui/src/components/community/announcements-list.tsx: the inspected announcement editor and Page DataSelector. appengine/src/community/community-announcement.service.ts requires further access-control review before relying on audience labels as restrictions.
  • appmint_go/event_app/lib/services/api_service.dart and the customer screens: member-app integration source. This course does not present its API exercises as native-app footage.

Where next

Evidence and current coverage

Practiced 18 September 2026 on local Studio 0.6.2 with the tutorial organization learnmu4qn1ha. Browser interaction created and reloaded Studio Circle, inspected members and posts, reviewed a report, flagged/removed/restored the fictional promotional post, and opened the remaining management screens. Real customer API sessions performed joins, post creation, original-image upload and attachment, commenting, reaction toggling, following, a poll and duplicate-vote rejection, messaging before connection, connection acceptance, meeting prerequisite failure and success, reporting and duplicate-report rejection. No real customers were contacted.

Historical pre-fix policy test: a separate fictional private page was readable anonymously; a fresh member joined despite approval-required and posted despite admins-only/pre-approve. An existing member’s second-page join failed with a unique-name error. API page creation saved the page before its owner-membership operation failed. A test script then continued with a missing ID and created one global practice post; that is a harness error, not a successful page-policy test. The corrected test used the persisted page ID and a fresh member. Both traces are retained so the failure is not hidden.

Captures and timestamped screen evidence: assets/appmint-community/evidence.json. API results: member-api-evidence.json, member-relations-evidence.json, moderation-readback-flagged.json, moderation-readback-removed.json, moderation-readback-restored.json, policy-final-evidence.json, poll-follow-evidence.json, and the media result files. Signed media URLs were removed from the public evidence. Record IDs are in fixture-ids.json; passwords and tokens are not in these assets.

Member actions in this course were exercised through API requests, not a verified member website or native EventOxygen screen. Announcement submission was blocked in the original 18 September run. The 21 September repair and independent rehearsal successfully saved and reopened a draft. Group-chat creation, badge awards, push delivery, story creation/expiry and event auto-joining were not completed here. These limits are reflected in the instructions and the video guide rather than filled with assumed clicks.

Independent local rehearsal — 21 September 2026

Current repair report and live evidence supersede the historical defects where explicitly stated. The current run used a separate isolated organization and three new training identities. Public conversation, the developer lab, moderation and announcement draft readback were exercised. The 24 September continuation verifies privacy, approval and posting enforcement and fixes canonical hashtag storage. Current policy and hashtag acceptance. Older images remain labeled as historical, not proof of repaired behavior.