Wbcom Designs Jetonomy Docs
Back to product Buy Now

Getting Started

Install, configure, and launch your community in minutes.

Get Jetonomy running on your WordPress site in under five minutes. This guide covers system requirements, how to install, and what happens the moment you activate.

Jetonomy setup wizard guiding you through initial configuration

What You Will Learn

  • Whether your server meets the requirements
  • Three ways to install Jetonomy
  • What Jetonomy sets up automatically on first activation

See it running first - community.wbcomdesigns.com is Wbcom's own support community, running Jetonomy in production. Browse the spaces, read the threads, and see how topics, replies, voting, trust levels, and moderation feel on a live site before you install. Public registration is open, so you can sign up and ask a question there if you have one.

Requirements

Jetonomy requires a modern WordPress stack. Check these before installing.

Requirement Minimum
WordPress 6.7 or higher
PHP 8.1 or higher
MySQL 5.7 or higher (or MariaDB 10.4+)
Browser Any modern browser (Chrome, Firefox, Safari, Edge)

Jetonomy works with any WordPress theme. For the best visual result with zero extra configuration, use BuddyX.

Note: Jetonomy does not use WordPress custom post types. It stores all community data in its own optimized database tables (wp_jt_*). This is intentional - it gives your community the query performance and scalability that CPT-based plugins cannot match.

Installation

Method 1: WordPress Admin (Recommended)

  1. Go to Plugins → Add New Plugin in your WordPress admin.
  2. Search for Jetonomy.
  3. Click Install Now, then Activate.

Method 2: Upload a ZIP File

  1. Download the Jetonomy ZIP from jetonomy.com or wordpress.org.
  2. Go to Plugins → Add New Plugin → Upload Plugin.
  3. Choose the ZIP file and click Install Now, then Activate.

Method 3: WP-CLI

wp plugin install jetonomy --activate

What Happens on Activation

Jetonomy sets everything up automatically the first time you activate it. You do not need to run any SQL or configure anything manually.

Database tables created (24 total):

Jetonomy creates 24 custom tables under the wp_jt_ prefix, one for each data entity: categories, spaces, posts, replies, votes, user profiles, notifications, subscriptions, tags, moderation flags, revisions, invite links, bookmarks, and more.

WordPress capabilities registered:

Jetonomy registers 20 custom capabilities (jetonomy_read, jetonomy_create_posts, jetonomy_moderate, and others) and maps them to your existing WordPress roles automatically.

Permalink rules flushed:

Your community URLs (e.g. yoursite.com/community/) are registered and rewrite rules are flushed immediately. No manual permalink reset needed.

URL Note: Throughout this documentation, /community/ is used as the default base URL. You can change this to any slug (e.g. /forum/, /discuss/, /hub/) in Jetonomy → Settings → General → Community Base URL. All community URLs automatically update when you change the base slug.

Cron jobs scheduled:

Two background jobs are scheduled via WP-Cron:

  • Trust level evaluation - runs every 12 hours to promote members who have earned higher trust levels.
  • Notification digests - runs daily and weekly (Jetonomy Pro).

After activation, you will see a blue notice at the top of your dashboard:

Your community is almost ready. Run the setup wizard to get started.

Click that notice to launch the setup wizard and go live.

Tip: If you are migrating from bbPress, wpForo, or Asgaros Forum, activate Jetonomy first to complete setup, then use the importer at Jetonomy → Import. Your existing data is never touched until you explicitly start an import.

WordPress Multisite

Jetonomy is multisite-compatible. When you network-activate the plugin from Network Admin → Plugins, Jetonomy automatically creates all required wp_jt_* database tables on every existing subsite in the network.

New subsites created after network activation are also provisioned automatically. The moment WordPress adds a new subsite to the network, Jetonomy detects it and creates the required tables before any community activity can occur.

Note: Each subsite has its own independent set of wp_{blog_id}_jt_* tables and its own community data. Spaces, posts, and members are not shared across subsites.

If you prefer per-site activation rather than network activation, install and activate Jetonomy on each subsite individually. Both approaches work correctly.

Uninstalling

If you deactivate Jetonomy, your data is preserved. Only a full uninstall (delete) removes the wp_jt_* tables, all plugin options, and all registered capabilities, giving you a clean removal with no database debris.

What's Next?

Run the three-step setup wizard to choose your community URL, create your first space, and go live.

Run the Setup Wizard →

After you activate Jetonomy, a three-step wizard walks you through the only decisions you need to make before your community goes live. The whole process takes about two minutes.

Jetonomy setup wizard with step-by-step configuration

What You Will Learn

  • How to set your community URL slug
  • How to choose between creating your first space manually or loading demo data
  • What the wizard does, and what you can always change later

Opening the Wizard

Click the blue notice at the top of your WordPress dashboard, or go to Jetonomy → Dashboard and click Launch Setup Wizard.

The wizard runs in a full-screen overlay. You can close it at any time. Your progress is saved, and you can return to finish it later.

Step 1: Community URL

Choose the slug where your community will live on your site.

The default is community, which gives you yoursite.com/community/. You can change this to anything that fits your site: forum, hub, members, discuss, or your brand name.

Example slug Resulting URL
community yoursite.com/community/
forum yoursite.com/forum/
hub yoursite.com/hub/

Default space type: Also on this screen, choose the default type for new spaces you create. Your options are:

  • Forum - open-ended threaded discussion
  • Q&A - questions with votable answers; the best answer can be marked accepted
  • Ideas - feature requests and votes with a status-lane roadmap
  • Show & Tell - short-form cards for sharing projects and work

You can create spaces of any type regardless of what you choose here. This setting just controls the default when you click "Add Space" later.

Tip: You can change your community URL slug later in Jetonomy → Settings → General. Jetonomy automatically flushes permalink rules when you save.

Step 2: First Space

This step gets real content into your community so it is ready to share the moment you finish. Choose the path that fits where you are right now.

Path A: Create Your First Space

Choose this if you are setting up a production site and want to start with your own content.

  1. Enter a name for your first space (e.g., "General Discussion").
  2. Choose its type: Forum, Q&A, or Ideas.
  3. Set a join policy: Open (anyone can join), Request to join (members need approval), or Invite only.
  4. Click Create Space.

Your space is created and visible immediately after you finish the wizard.

Path B: Load Sample Data

Choose this if you want to explore Jetonomy's features before committing to a structure.

Jetonomy seeds your site with:

  • 2 categories - "Product Support" and "Community"
  • 5 spaces - one of each type (Forum, Q&A, Ideas) plus two extras with varied content
  • Demo users with realistic avatars, trust level badges, and posting history
  • Sample posts and replies - enough content to see voting, accepted answers, tags, and notifications working in context

This lets you experience the full community interface as a regular member would see it, without writing any content yourself.

When you are ready to go live, click Remove Demo Data on the Jetonomy dashboard. Every demo post, reply, space, category, and user record is deleted in a single operation. Any real content you added alongside the demo data is preserved.

Note: Demo data is tracked internally via a jetonomy_demo_data record. Removal is precise and does not affect any content you created yourself.

Step 3: Done

The final screen confirms your community is live and gives you two quick links:

  • Visit your community - opens yoursite.com/community/ in a new tab so you can see the frontend immediately.
  • Go to admin dashboard - takes you to Jetonomy → Dashboard where you can manage spaces, moderate content, and configure settings.

Everything you configured in the wizard can be changed later:

Setting Where to change it
Community URL slug Jetonomy → Settings → General
Default space type Jetonomy → Settings → Spaces
Space name, type, join policy Jetonomy → Spaces → Edit
Email notifications Jetonomy → Settings → Notifications

What's Next?

Your community is live. Now learn what to do in the first hour: create categories, set up your real spaces, and invite your first members.

Your First Community →

Your community is installed and the wizard is complete. This guide walks you through what to do next, from organizing your spaces to inviting your first members, so your community is genuinely ready for people on day one.

Community home page showing spaces organized by category

What You Will Learn

  • How to organize spaces with categories
  • How to create your first real space and choose the right type
  • How to invite members with a shareable link
  • How to customize the look and feel
  • How to import from bbPress or wpForo if you are migrating
  • What the community frontend looks like for your members

Create Categories to Organize Your Spaces

Categories are the top-level groupings in your community. Every space belongs to a category. Before you create more spaces, take a moment to plan your category structure - it is much easier to do now than to reorganize later.

To create a category:

  1. Go to Jetonomy → Categories.
  2. Enter a name and optional description in the form on the left.
  3. Click Add Category.

Your category appears in the table on the right. Drag rows to reorder them. The order here is the order your members see on the community home page.

Tip: Start with two to four broad categories. You can always add more later. Common patterns: "Support / General / Announcements" for a product community, or "Ideas / Questions / Showcase" for a creator community.

Create Your First Real Space

A space is where discussions happen. Each space has a type that shapes how members interact with content.

Choosing a Space Type

Type Best for Key feature
Forum General discussion, announcements, support Threaded replies, newest/popular sort
Q&A Technical help, knowledge bases Votable answers, accepted answer highlight
Ideas Feature requests, roadmaps Status lanes (Planned, In Progress, Shipped, Declined) with roadmap view
Show & Tell Project showcases, member work Card feed with optional title, reactions, and votes

To create a space, you can use either the wp-admin form or the front-end Create Space page. Both produce the same result.

From wp-admin:

  1. Go to Jetonomy → Spaces → Add Space.
  2. Enter a name and optional description.
  3. Pick an icon from the visual Lucide picker (16 defaults plus a search field).
  4. Choose the space type.
  5. Set visibility: Public (anyone can see it), Private (members only see content), or Hidden (not listed, invite only).
  6. Set the join policy: Open, Request to join, or Invite only.
  7. Click Save Space.

From the front end (so non-admin owners can create spaces too): visit /community/new-space/ while signed in. The form is identical and is available to any role you've enabled under Settings → Front-end space creation.

Your space is immediately available on the community frontend under its category.

Invite Members

You do not need to wait for members to discover your community organically. Jetonomy gives you a direct invite link you can share anywhere.

Generate an Invite Link

  1. Go to Jetonomy → Spaces and click on your space.
  2. Click Members in the space navigation.
  3. Click Generate Invite Link.
  4. Set an expiry date, or leave it open with no expiry.
  5. Copy the link and share it via email, Slack, social media, or anywhere else.

When someone visits the link, they are added to the space immediately after logging in or creating a WordPress account.

Note: Invite links work for any WordPress user registration flow you have configured. If you allow open registration, new members can sign up and join in one step.

Existing WordPress Users

Anyone who already has an account on your WordPress site can visit yoursite.com/community/ and join public spaces by clicking Join Space. Their existing avatar, display name, and email are used automatically.

Customize the Appearance

Jetonomy inherits your theme's fonts, colors, and border radius automatically via WordPress theme tokens. If you are using BuddyX, this integration is immediate. Jetonomy reads BuddyX's design tokens and matches your brand without any manual work.

To adjust further:

  • Go to Jetonomy → Settings and explore the General and Advanced tabs.
  • To override specific templates, create a jetonomy/ folder inside your active theme directory and drop in any template file from wp-content/plugins/jetonomy/templates/. Jetonomy always checks your theme folder first.

Tip: You do not need to copy all templates. Only override the ones you want to change. Unmodified templates are served directly from the plugin.

Importing from bbPress, wpForo, or Asgaros

If you are migrating an existing community, Jetonomy includes a built-in importer for three sources.

  1. Go to Jetonomy → Import.
  2. Select your source plugin: bbPress, wpForo, or Asgaros Forum.
  3. Jetonomy auto-detects your existing data and shows a summary, for example: "Found: 12 forums, 3,847 topics, 28,419 replies."
  4. Run a Dry Run first to check for any mapping issues.
  5. Click Start Import when you are ready.

Imports run in background batches. You can close your browser and come back. The import continues via WP-Cron and resumes from where it left off if interrupted.

What gets migrated:

Source Jetonomy
Forums Categories + Spaces
Topics Posts
Replies Replies
Users WordPress users + Jetonomy profiles

The Community Frontend: A Quick Tour

Once you have content, here is what your members will see.

Community Home (/community/)

The home page lists all categories with their spaces. Each space card shows the post count, member count, and a recent activity indicator. Members can sort by activity or browse by category.

Space Listing (/community/s/space-slug/)

Inside a space, members see a topic list with vote scores, reply counts, author avatars, tags, and time. They can filter by Latest, Popular, or Unanswered. A New Post button appears in the top right for members who have permission to post.

Single Topic (/community/s/space-slug/t/topic-slug/)

The topic view shows the full post, vote buttons, and all replies. Replies are threaded up to three levels deep. For busy topics with many replies, Jetonomy loads the first 10 and last 10 replies by default, with a gap-loader button in between to fetch more. This keeps page load fast regardless of reply count.

In Q&A spaces, the accepted answer is pinned to the top and highlighted with a green checkmark.

Sidebar

The community sidebar (where the theme layout places it) shows active members, trending tags, and recent activity. The exact sidebar layout depends on your theme.

Note: All community pages are server-side rendered. There are no JavaScript-only pages, so every URL is indexable by search engines out of the box.

What's Next?

Now that your community is live and populated, learn how to organize it further with spaces and categories, including visibility rules and per-space permissions.

Spaces and Categories →

Before Jetonomy 1.4.0, anything that required a logged-in member bounced visitors to wp-login.php. That meant signing in to upvote a post or reply to a thread sent visitors to a generic WordPress login screen, then back to the community after a redirect. From 1.4.0 onward, Jetonomy handles Login, Register, and Forgot Password in-page through its own /auth/* REST endpoints, with forms that match your theme and the rest of the community UI.

What You Will Learn

  • Where the in-page auth forms appear and how they behave
  • Why the new flow matters for member experience
  • How captcha protection now covers signup, not just posting
  • Who still uses wp-login.php and why
  • How auth behaves in Private community mode
  • How to customize the auth surface with theme tokens

Where the Forms Appear

In-page auth shows up in two ways depending on what the visitor is doing.

Modals on Interaction

When a signed-out visitor tries to do something that requires an account, Jetonomy opens a modal directly over the page they're on. The modal carries Login, Register, and Lost Password tabs.

Common triggers:

  • Clicking the upvote arrow on a post or reply
  • Starting a reply
  • Clicking "Follow" on a space or member
  • Trying to subscribe to a tag
  • Clicking "Bookmark"

After successful sign-in, the modal closes and the action the visitor was trying to take happens automatically. They never lose their place.

Dedicated Pages

Three full-page routes are always available:

Route Purpose
/community/login/ Direct link for sign-in
/community/register/ Direct link for new accounts
/community/lost-password/ Direct link for password reset

These pages are useful for sharing in marketing emails, embedding in your nav, or sending in support replies. They use the same forms and the same /auth/* endpoints as the modal, so behaviour is consistent.

Why This Matters

The old wp-login.php flow worked, but it had three real problems:

  1. Visual jarring. The WordPress login screen does not look like your community. Visitors went from your themed pages to a generic blue-and-white form and back. The break in visual continuity made the community feel less polished.
  2. Lost context. Visitors who clicked "Reply" had to sign in, then find their way back to the thread. WordPress's redirect handling did not always land them on the right page, especially with theme-specific URLs.
  3. Slow perceived load. Two full page navigations for what should be a quick "sign me in and let me reply" step.

In-page auth fixes all three. The forms render where the visitor is, look like the rest of the community, and pick up the visitor's intended action when they finish signing in.

Captcha Now Protects Signup

Site owners can configure reCAPTCHA or Cloudflare Turnstile keys under Jetonomy → Settings → Anti-spam. Before 1.4.0, those keys only protected post and reply submission. Bots could still register accounts freely.

From 1.4.0, the same captcha keys also protect:

  • New account registration
  • Password reset requests
  • Login attempts after repeated failures from the same IP

Nothing needs to change in your settings. If you already had captcha configured, it now extends to signup automatically. If you don't have captcha configured, the auth forms still work; they just have less spam protection.

Captcha Providers Supported

Provider Where to get keys
Google reCAPTCHA v3 https://www.google.com/recaptcha/admin
Cloudflare Turnstile https://dash.cloudflare.com/?to=/:account/turnstile

Choose whichever fits your stack. Turnstile is recommended if you're privacy-conscious or already on Cloudflare; it runs without challenging visitors in most cases.

Who Still Uses wp-login.php

In-page auth covers your community. The standard WordPress site-wide login is unchanged.

Administrators (anyone with the manage_options capability) can still sign in at wp-login.php and reach wp-admin/. That's intentional. Site owners need a reliable way to get into the admin area even if community pages have an issue, and security plugins, two-factor plugins, and SSO integrations all hook into wp-login.php.

In practice:

  • Members never need to visit wp-login.php
  • Admins can use either wp-login.php (for admin access) or /community/login/ (to log in as a member)
  • Any plugin you have that customises wp-login.php (login restrictions, two-factor, branding) still works for admin login

Private Community Mode

If you've set your community to Private (under Jetonomy → Settings → Privacy), signed-out visitors can only reach three pages:

  • /community/login/
  • /community/register/
  • /community/lost-password/

Every other community URL redirects to /community/login/ with a redirect_to parameter, so the visitor lands on the page they were trying to reach as soon as they sign in.

Registration can be disabled separately if you only want to allow invited members. In that case the Register tab is hidden, and /community/register/ redirects to /community/login/.

Customization

The auth surface uses the same --jt-* design tokens as the rest of Jetonomy. That means your theme's brand color, fonts, border radius, and spacing are picked up automatically. No custom CSS required for a polished match.

Light Auth Surface in Dark Mode

There's one intentional exception: the Login Block and the modal auth forms stay in light mode even when the rest of your community is in dark mode. This is a deliberate UX choice. Sign-in forms in dark mode are statistically harder to read and easier to mistype, especially on mobile. Keeping auth surfaces light maintains form readability where it matters most: at the point of conversion.

If you want to override this and run a dark auth surface (for a fully dark community theme), you can do it via CSS:

.jt-auth-modal,
.jt-login-block {
  --jt-bg: #1a1a1a;
  --jt-text: #f5f5f5;
}

Customising Form Labels

All auth form labels are translatable through the standard WordPress translation pipeline. They use the jetonomy text domain. If you're running a translated site, the forms pick up your translations on the next load.

Replacing the Forms Entirely

For deep customisation (e.g. adding a "Sign in with Google" button via your SSO plugin), the auth templates are theme-overridable:

your-theme/jetonomy/auth/login-form.php
your-theme/jetonomy/auth/register-form.php
your-theme/jetonomy/auth/lost-password-form.php

Copy from wp-content/plugins/jetonomy/templates/auth/ to start.

What's Next?

Learn how Jetonomy's in-product modal toolkit replaces native browser dialogs across the community.

Modals and Confirmations

Spaces & Categories

Organize your community into discussion spaces.

Spaces are the discussion areas inside your community - each one has its own topic listing, member list, and settings. This guide shows you how to create, configure, and manage them.

Admin space editor with name, description, and settings fields

What You Will Learn

  • What spaces are and how they relate to categories
  • Every field on the space creation form
  • How the space header looks on the frontend
  • How to edit and archive an existing space

What Is a Space?

A space is a focused discussion area organized around a single topic or purpose. Examples: a "General Discussion" space, a "Product Feedback" space, a "Help & Support" space.

Every space belongs to a category. Categories are the top-level groupings (like tabs or sections) that members see on the community home page. A space without a category will not appear in the community navigation.

Tip: Plan your space structure before creating anything. Too many spaces fragment your community early. Start with three to five and add more as demand grows.

Creating a Space

You have two ways to create a space, depending on who needs to do it:

  • wp-admin path (admins): Go to Jetonomy → Spaces in your WordPress dashboard and click Add New Space.
  • Front-end path (anyone you choose): Visit /community/new-space/ while signed in. This page is available to any user role you've allowed under Settings → Front-end space creation, so you can let space owners or moderators create spaces without giving them wp-admin access. See Creating Spaces From the Front End for the full walkthrough.

Both paths show the same fields and produce the same result. The rest of this section describes each field.

Basic Information

Title - The name members see in listings, the space header, and breadcrumbs. Keep it short and descriptive.

Slug - The URL-safe identifier for this space. Jetonomy auto-generates a slug from the title. The final URL will be /community/s/your-slug/. You can change the slug, but doing so after posts exist will break any existing links.

Description - A short sentence or two explaining what this space is for. This appears in the space header below the title and in category listing cards. It also populates the meta description for search engines.

Icon - The space icon is selected from Jetonomy's built-in Lucide icon picker (16 default icons plus 8 extras revealed by the "Show more" button, plus a search field to find any other Lucide name). The chosen icon appears in the space header and on category listing cards alongside the title. Icons render as crisp SVGs at every size, so they read clearly on mobile.

Category - Select which category this space belongs to. A space must be assigned to a category to appear on the community home page.

Space Configuration

Type - Choose Forum, Q&A, or Ideas. This controls how posts and replies behave. See Space Types for a full explanation of each.

Visibility - Controls who can see the space and its content. Options: Public, Private, or Hidden. See Membership & Join Policies for details.

Join Policy - Controls how members gain access. Options: Open, Approval Required, or Invite Only.

Click Save Space to publish it immediately.

The Space Header (Frontend)

Every space has a header at the top of /community/s/your-slug/ showing:

  • The Lucide icon at large size
  • The space title and description
  • A stat bar with total post count, member count, and last activity time
  • A Follow button for logged-in users (subscribes them to new post notifications)
  • A Join button when the space requires membership

Members who have already joined see the Join button replaced with their role badge (Member, Moderator, or Admin).

Editing a Space

Two paths, same fields:

  • wp-admin: Go to Jetonomy → Spaces, find the space in the list, and click Edit.
  • Front end: Visit the space at /community/s/your-slug/ while signed in as a space owner / moderator and click the Edit space button in the space header. See Editing Spaces From the Front End for screenshots.

All fields are editable from either path, including the type and visibility settings.

Note: Changing the space type after content exists does not reformat old posts. Existing posts keep their original structure. Only new posts use the new type's behavior.

Archiving a Space

To archive a space, open it for editing and set its Status to Archived. An archived space becomes read-only - members can read existing posts and replies, but cannot create new ones. The space remains visible in listings with a clear "Archived" label.

Archived spaces do not count toward activity stats on the community home page.

To permanently remove a space, click Delete in the space list. This action also deletes all posts, replies, votes, and member records inside that space. It cannot be undone.

What's Next?

Learn how each space type changes the way posts and replies behave.

Space Types →

The space type you choose determines how posts are structured, how replies work, and what extra features appear. Pick the right type and your community will feel purpose-built - pick the wrong one and members will feel like they are fighting the interface.

Q&A space showing questions with accepted answers and vote counts

What You Will Learn

  • What each space type is designed for
  • How posts and replies behave differently per type
  • The unique features each type unlocks
  • How to change a space type after creation

The Five Space Types

Forum

Forum is the default type. It is the right choice for general discussion, support, announcements, or any conversation without a single "correct" answer.

How it works:

  • Members post a topic with a title and rich content.
  • Replies thread up to three levels deep (reply to a reply to a reply).
  • Votes on replies surface the best contributions via the Best sort, but no reply is formally "accepted."
  • Topics can be sorted by Newest, Oldest, or Best on the space listing page.

Use Forum for: support channels, general discussion, community announcements, staff Q&A sessions.

Q&A

Q&A is built for questions that have a definitive best answer. It follows the model made popular by Stack Overflow. The person who asked the question marks one reply as the accepted answer.

How it works:

  • Every post is a question. The title should be phrased as a question.
  • Replies are answers. Each answer is voted on independently.
  • The post author sees an Accept button on every reply. Clicking it marks that reply as the accepted answer and pins it to the top of the reply list, regardless of sort order.
  • The accepted answer author earns a reputation bonus (+15 points).
  • The post listing shows an "Answered" badge on topics with an accepted answer. This badge also appears on the space list so members can see at a glance which Q&A spaces have resolved questions.

Use Q&A for: help & support, how-to guides, technical documentation requests, troubleshooting.

Tip: The Unanswered filter on the space listing page shows only topics with no accepted answer. This is a powerful tool for community moderators and support teams tracking open questions.

Ideas

Ideas is built for feature requests, product feedback, and roadmap voting. Each idea has a status that you control, and members vote to indicate demand.

How it works:

  • Members submit ideas with a title and description.
  • Other members upvote (or downvote) to signal interest. Vote score drives the default sort order.
  • Each idea has an Idea Status that the space moderator or admin updates manually:
Status Meaning
Planned On the roadmap
In Progress Being built right now
Shipped Completed and available
Declined Will not be implemented
  • Status updates appear in the reply thread as a system activity entry, so members can see when an idea's status changed.
  • The space listing page has a filter bar showing counts per status, making it easy to browse the roadmap.

Use Ideas for: product feedback boards, feature request trackers, community roadmaps, vote-to-prioritize workflows.

For a full guide to the Ideas roadmap view, see Ideas Roadmap.

Tip: Pick the lightbulb icon from the Lucide icon picker and name the space something like "Ideas & Feedback" to set the right expectation before members click through.

Show & Tell

Show & Tell is designed for members to share work, projects, and creations with the community. Think of it as a portfolio wall where members post a short card showcasing something they made, and others react, comment, and vote.

How it works:

  • The post title is optional. A strong visual or a single-sentence description is all a member needs to post.
  • Posts appear as cards in a clean grid or list feed, sorted chronologically by default.
  • Replies are supported for comments and feedback, threaded up to three levels deep.
  • Voting is available. The Popular sort surfaces the most appreciated posts.

Use Show & Tell for: member projects, weekly challenges, course work showcases, portfolio highlights, "what I built" feeds.

Tip: Show & Tell works well paired with a Q&A or Forum space in the same community. Members can share work in Show & Tell and ask follow-up questions in a companion space.

For a full guide to Show & Tell, see Show & Tell Spaces.

Social Feed

Social Feed is designed for short-form status updates - brief posts without a required title. It behaves more like an activity stream than a traditional forum.

How it works:

  • The post title field is optional. Members can post a standalone message, image, or link.
  • Posts appear in a card-style feed sorted chronologically by default.
  • Replies work the same as Forum type, threaded up to three levels deep.
  • Voting is available, but the feed sort does not default to Best - it defaults to Latest.

Use Social Feed for: member introductions, community announcements, daily check-ins, open-ended community updates.

Note: Social Feed is available in Jetonomy 1.0. Advanced social features including @mentions in feeds and rich link previews are planned for version 1.1.

Changing the Space Type

You can change the type of an existing space at any time. Either open it in Jetonomy → Spaces in wp-admin, or use the Edit space button on the space header itself (front-end edit), and update the Type field.

The change takes effect immediately for all new posts. Existing posts keep their original structure. A Q&A post does not lose its accepted answer, and an Ideas post does not lose its status history.

If you change a Q&A space to Forum, the Accept button disappears from the UI but existing accepted answers remain stored in the database.

What's Next?

Learn how to control who can see your spaces and how members join them.

Membership & Join Policies →

Every space has two independent controls: who can see it (visibility) and how members get in (join policy). Combining them gives you precise control over every access scenario - from fully public forums to invite-only private communities.

Permissions settings panel with trust level thresholds and rate limits

What You Will Learn

  • The three visibility levels and when to use each
  • The three join policies and how they differ
  • Per-space posting restrictions
  • Space member roles and what each can do
  • How to combine visibility and join policy for common use cases

Visibility Levels

Visibility controls whether the space appears in listings and whether non-members can read its content.

Public

The space appears on the community home page, in search results, and in category listings. Any visitor - including users who are not logged in - can read all posts and replies.

Members still need to join (or be approved) before they can post or reply, depending on your join policy.

Use Public when you want maximum reach and SEO value. Most community spaces should start here.

Private

The space appears in listings and search results, so members can discover it. However, only approved members can read the content. Non-members see the space name and description, then a "Request to Join" prompt.

Use Private for paid membership communities, internal team discussions, or beta program spaces where content should be gated but the space itself should be findable.

Hidden

The space does not appear in any listing, search result, or category navigation. Only members who have already joined - and admins - can see it.

Members can only reach a hidden space via a direct link or an invite link you share with them.

Use Hidden for admin-only spaces, private moderator discussion boards, or early access groups where you control every invitation.

Note: WP Admins and space admins can always see hidden spaces in the admin panel, regardless of their membership status.

Join Policies

Join policy controls how members gain access to the space. It works in combination with visibility.

Open

Any logged-in user can join instantly by clicking the Join button on the space header. There is no approval step.

Members who join an Open space can post and reply immediately (subject to your per-space posting restrictions).

Use Open for general discussion spaces, community-wide help channels, and any space where you want minimal friction.

Approval Required

When a user clicks Join, they submit a join request. The request goes to the space moderators and admins for review.

Moderators see pending requests in Jetonomy → Moderation → Join Requests. They can approve or decline each request. The user gets a notification when their request is reviewed.

Approved members can then post and reply immediately.

Use Approval Required when you want to vet members before they can participate - for example, a verified customer support channel or a professional community with admission criteria.

Invite Only

No join button is shown to non-members. Members can only enter via an invite link generated by a space moderator or admin.

To create an invite link, open the space in the admin panel and go to the Invite Links section. Each link has a configurable usage limit and optional expiry date. You can see how many times each link has been used.

Anyone who visits a valid invite link is automatically added as a member - no approval step required.

Use Invite Only for private groups, course cohorts, or closed communities where every member should be explicitly invited.

Per-Space Posting Restrictions

Beyond join policy, each space has two additional settings that control what members can do once inside:

Who Can Post - Controls who can create new topics in this space.

Setting Effect
Anyone (members) All members can post
Members with trust level 1+ New members (trust level 0) cannot post
Moderators only Only space moderators and admins can post

Who Can Reply - Controls who can reply to existing topics.

Setting Effect
Anyone (members) All members can reply
Members with trust level 1+ New members cannot reply
Moderators only Only space moderators and admins can reply

These settings let you create announcement-only spaces (post and reply both set to Moderators only), or read-heavy Q&A spaces where only trusted members can contribute.

Space Member Roles

Every member of a space has one of three roles:

Member - Can read, post, and reply within the space's configured restrictions. Can vote, follow, and bookmark topics.

Moderator - All member abilities plus: approve pending posts, pin topics, close topics, move topics, delete posts and replies, manage join requests, create invite links, and review flagged content within the space.

Admin - All moderator abilities plus: change space settings, assign moderator and admin roles to other members, manage access rules, and archive or delete the space.

Note: WordPress site admins can perform all admin-level actions on any space, regardless of their space role.

Promoting members from the front-end (1.3.8+)

Space admins (and site admins) can change a member's space role directly from the front-end members page at /community/s/:slug/members/. Each member row carries a role dropdown; picking a new role saves the change live with inline success or error feedback. There is no wp-admin round-trip to add a moderator and no separate settings screen to open.

The dropdown is hidden for members who cannot manage roles, and a member cannot demote themselves below the level needed to keep at least one space admin. Members visible in the list still have to be members of the space to be assigned a role; the same per-space role rules listed above apply.

Common Visibility + Join Policy Combinations

Goal Visibility Join Policy
Public community forum Public Open
Paid membership forum Private Approval Required
Team-only internal channel Hidden Invite Only
Verified customer support Public Approval Required
Early access beta group Hidden Invite Only
Course community Private Open (link-gated via Invite Only)

What's Next?

See every per-space setting in one place, including how they override global defaults.

Space Settings →

Each space can override the global Jetonomy defaults with its own settings. This page is a complete reference for every per-space option, how it interacts with global settings, and how invite links work.

Admin space editor showing per-space configuration options

What You Will Learn

  • Every per-space setting and what it controls
  • How per-space settings override global defaults
  • How to require moderator approval before posts go live
  • How to create, share, and track invite links

Accessing Space Settings

Go to Jetonomy → Spaces in your WordPress admin, find the space, and click Edit. The settings panel is on the right side of the edit screen.

Per-Space Settings Reference

Posts Per Page

Default: Inherits from global setting (default: 20)

Overrides how many topics appear per page on this space's listing. Set a lower number for low-traffic spaces with long post titles. Set a higher number for high-activity spaces where members scan quickly.

Valid range: 5 to 100.

Require Post Approval

Default: Off

When enabled, every new post submitted by a non-moderator is held in a pending state and does not appear publicly until a moderator approves it.

Moderators and space admins can see pending posts immediately in the space listing with a "Pending" label. They can approve, reject, or mark the post as spam from the topic view or from Jetonomy → Moderation.

The post author receives a notification when their post is approved or rejected.

Tip: Enable this for your early community days when you want to review every submission, then turn it off once you trust your membership base.

Allow Voting

Default: On (inherits from global)

Controls whether upvote and downvote buttons appear on posts and replies in this space. Disabling voting also removes vote scores from the space's topic listing.

In Q&A spaces, voting is always available on replies regardless of this setting - otherwise the Best sort and accepted-answer workflow cannot function correctly.

In Ideas spaces, voting cannot be disabled because it is the core mechanism for prioritizing ideas.

Who Can Post

Default: Anyone (members)

See Membership & Join Policies for the full option set. The per-space value overrides the global default for this space only.

Who Can Reply

Default: Anyone (members)

Controls who can add replies to topics in this space. Overrides the global default for this space only.

How Per-Space Settings Override Global Settings

Jetonomy uses a two-layer settings system:

  1. Global settings - Set at Jetonomy → Settings → Community. These are the defaults that apply to every space.
  2. Per-space settings - Set on individual spaces. When a per-space value is configured, it takes precedence over the global value for that space only.

If you leave a per-space setting at "Inherit from global," any future changes to the global setting will automatically apply to that space. If you explicitly set a per-space value, global changes do not affect it.

This means you can configure a sensible default globally and only override the spaces that need different behavior - instead of configuring every space individually.

Access Rules for Membership-Gated Spaces

For Private and Hidden spaces, you can restrict access based on external membership status - not just whether someone has joined the space.

Go to the Access Rules tab on the space edit screen to add rules.

Each rule has three parts:

Rule Type - What to check:

Type What it checks
Logged In User is authenticated
WordPress Role User has a specific WP role (e.g. Editor)
Capability User has a specific WP capability
Trust Level User's Jetonomy trust level (0-5)
MemberPress User has an active MemberPress membership
Paid Memberships Pro User has an active PMPro level

Access Grant - What to allow:

Grant Effect
Read Can view posts and replies, cannot participate
Participate Can read, post, and reply
Full All participate abilities plus moderator actions

Auto-Assign Role - Optionally assign the member a space role (Member, Moderator, Admin) automatically when the access rule is satisfied. This is useful when you want MemberPress Gold members to automatically become space moderators.

Multiple rules can be stacked. Jetonomy grants the highest matching permission level.

Note: MemberPress and Paid Memberships Pro adapters are available in Jetonomy free. WooCommerce Memberships, Restrict Content Pro, and LearnDash adapters require Jetonomy Pro.

Invite Links

Invite links let you bring specific people into a space without opening up general membership.

Creating an Invite Link

  1. Open the space for editing and go to the Invite Links section.
  2. Click Create Invite Link.
  3. Set an optional Usage Limit (how many people can use this link before it expires).
  4. Set an optional Expiry Date.
  5. Click Generate.

Jetonomy generates a unique URL: /community/invite/abc123def/

Sharing an Invite Link

Copy the link from the Invite Links table and share it however you prefer - email, Slack, a membership welcome email, etc.

When someone visits the link, they are prompted to log in if they are not already. After logging in, they are automatically added to the space as a Member.

Tracking Usage

The Invite Links table shows each link's current usage count against its limit. Links that have reached their usage limit are automatically deactivated but remain in the table for your records.

You can manually deactivate or delete any invite link at any time.

What's Next?

Learn how to create topics and posts inside your spaces.

Creating Topics →

Show & Tell spaces are built for members to share what they have made. Where a Forum space is about discussion and a Q&A space is about solving problems, Show & Tell is about visibility - giving members a dedicated place to publish projects, creative work, course completions, or anything worth sharing with the community.

What You Will Learn

  • What a Show & Tell space is and when to use one
  • How the optional title and inline card layout work
  • How it differs from a Forum space and a Social Feed space
  • What settings are available for Show & Tell spaces

What It Is

A Show & Tell space presents posts as cards in a clean feed. Each card represents one piece of shared work. Visitors can browse at a glance, vote on favorites, and reply with feedback or encouragement.

Think of it as a curated gallery where members are both the curators and the audience.

When to Use Show & Tell

Use case Why it fits
Member project gallery Cards keep posts scannable; members can vote for favorites
Course or challenge completions Members share their work as a milestone; replies act as peer review
Weekly showcase threads A predictable cadence of "what did you ship this week?" posts
Portfolio or portfolio-in-progress Members build a visible record of work over time
Community highlights and spotlights Admins feature great work; members see it without digging through discussions

If your primary need is open-ended conversation, use a Forum space. If you need to capture feedback and feature requests with status tracking, use an Ideas space.

Optional Title and Inline Cards

The post title in a Show & Tell space is optional. A member can post:

  • Just a title with rich content below
  • An image or embed with no title at all
  • A one-line caption with an attached file
  • A longer write-up with a title

Because the title is optional, the composer does not show a red asterisk or validation error if members leave it blank. Jetonomy uses the first line of content as a display label in the card feed if no title is provided.

Each post appears as a card in the space listing. The card shows the title (or a content excerpt), the author avatar, vote score, reply count, and time posted. Clicking the card opens the full post.

Layout

The space listing uses a card layout rather than the row layout used by Forum and Q&A spaces. Cards give more visual space to each post, which suits image-heavy or showcase content.

Members can sort by:

  • Latest - newest cards appear at the top (default)
  • Popular - highest vote score at the top

There is no Unanswered filter because Show & Tell posts are not questions.

How It Differs from Forum and Social Feed

Forum Social Feed Show & Tell
Post title Required Optional Optional
Primary layout Row list Compact feed Card grid/list
Default sort Latest Latest Latest
Best use Discussion Updates Showcasing work
Accepted answers No No No
Vote display Yes Yes Yes

The main difference between Show & Tell and Social Feed is intent. Social Feed is for quick updates and check-ins. Show & Tell is for finished or in-progress work that merits its own card and a longer shelf life. Both have optional titles, but Show & Tell's card layout makes each post feel like a dedicated showcase item rather than a stream entry.

Settings

Show & Tell spaces share the same settings as other space types:

  • Join policy - Open, Approval Required, or Invite Only
  • Visibility - Public, Private, or Hidden
  • Require Post Approval - hold new posts for moderator review before they appear
  • Voting - enable or disable voting on posts and replies

There are no Show & Tell-specific settings. All the general space settings in Space Settings apply.

What's Next?

Learn how the Ideas space type turns community feedback into a visual roadmap with status tracking.

Ideas Roadmap →

Every Ideas space includes a built-in roadmap view. Instead of scrolling through a flat list of feature requests, visitors can see all ideas organized by status - what is planned, what is being built, what has shipped, and what will not be pursued. This page explains the roadmap, the status lanes, and how status changes are communicated to your community.

What You Will Learn

  • Where to find the roadmap view
  • What the four status lanes mean
  • How admins update an idea's status
  • How status changes surface in notifications and the activity log
  • What the roadmap looks like for community members

What the Roadmap Shows

The roadmap is a dedicated view of an Ideas space that groups all ideas by their current status. Access it at:

/community/s/<space-slug>/roadmap/

A link to the roadmap appears in the Ideas space navigation alongside the main topic list. Members do not need to navigate manually - they can switch between the idea list and the roadmap from within the space.

Each status lane shows all ideas in that state, sorted by vote score within the lane (highest votes first). This gives members a clear view of what the community wants most and where each request stands.

Status Lanes

The roadmap has four lanes:

Status What it means
Planned The idea has been accepted and is on the roadmap. Work has not started yet.
In Progress The team is actively building or implementing this idea.
Shipped The idea has been completed and is now available.
Declined The team has decided not to pursue this idea. A reply with context is recommended.

New ideas submitted by members have no status by default. They appear in the main idea list but not in any roadmap lane until a moderator or admin assigns a status.

Tip: When you decline an idea, add a reply explaining why. Members who took the time to submit and vote on an idea deserve a clear answer. Declined ideas with a closing comment are far less likely to be re-submitted repeatedly.

How Admins Update a Status

Any space moderator or admin can change an idea's status:

  1. Open the idea (the single post view).
  2. Find the Status control in the post meta area below the title.
  3. Select a new status from the dropdown: Planned, In Progress, Shipped, or Declined.
  4. Click Update Status.

The status change saves immediately. A system entry appears in the reply thread showing what the status changed from and to, with a timestamp. This gives the idea's full history in one place.

You can also update status from the space admin panel at Jetonomy → Spaces → [Space Name] → Posts. The status column is editable inline from that view, which is useful for processing a batch of ideas at once.

How Status Changes Surface in Notifications

When an idea's status changes, Jetonomy sends notifications across three channels:

Activity log - A system activity entry is created in the idea's reply thread, visible to anyone who opens that idea.

Email digest - If a member follows the Ideas space and has email digest enabled, the status change is included in their next digest email (daily or weekly depending on their preference).

In-app inbox - The idea author receives an in-app notification immediately. All followers of the Ideas space also receive an in-app notification.

Members who do not follow the space will not receive notifications about that specific status change. Encourage members to follow the space after submitting an idea so they stay informed.

Customer-Visible Behaviors

What members see at each stage:

  • New idea with no status assigned - Appears in the idea list. Not shown in any roadmap lane until an admin or moderator picks a status. Vote buttons are active so members can build up signal even before the team triages.
  • Planned - Appears in the Planned lane on the roadmap. A "Planned" badge shows on the idea card.
  • In Progress - Moves to the In Progress lane. Badge updates. Members can see work has started.
  • Shipped - Moves to the Shipped lane. Badge shows "Shipped." Upvote button remains available so members can react positively to the delivery.
  • Declined - Moves to the Declined lane. Badge shows "Declined." Vote controls remain visible.

Ideas can be moved between statuses at any time. Moving a shipped idea back to In Progress (for a revision, for example) is valid and will notify followers again.

What's Next?

Learn about space membership policies - who can see a space, who can join, and how invite links work.

Membership & Join Policies →

Since Jetonomy 1.4.0, members with the right role can create a new space without ever opening wp-admin. The front-end Create Space page lives at /community/new-space/ and gives community owners a way to delegate space creation to trusted regulars, team leads, or paying members without handing out WordPress admin access.

What You Will Learn

  • Where the Create Space page lives and who can reach it
  • Which roles are allowed to create spaces, and how to change that
  • Every field available on the form, including the visual icon picker
  • What happens the moment the form is submitted
  • When to use this page versus the wp-admin equivalent

Where The Page Lives

The page is always available at /community/new-space/. It is part of the standard /community/ rewrite group, so it inherits the same theme, header, and footer as the rest of your community pages.

There is no separate menu item by default. Most communities expose the page in two places:

  • A "Create space" button on /community/ for signed-in users with permission
  • A "Start a space" link in the header avatar menu

Both links are conditional. Members who do not have permission never see the link and never see the page either, even if they type the URL directly.

Who Can Create Spaces

Open Jetonomy → Settings → Front-end space creation. The setting is a list of WordPress roles. Tick the roles you want to allow.

The default is Administrator only, which matches the pre-1.4.0 behaviour. Most communities widen this to Editor, Author, or a custom role such as "Community Builder" after they have run for a few weeks and identified trusted members.

A few important notes:

  • The permission is role-based, not per-user. If you want to grant one specific member the ability to create spaces, add them to a role that has it.
  • Granting the right to create a space is not the same as granting the right to moderate every other space. A member who can create one space only moderates the spaces they created, not the whole community.
  • Network admins on multisite have the permission everywhere by default.

The Form Fields

The front-end form mirrors the wp-admin space editor field-for-field. There is no "lite" version of space creation.

Field What it controls
Title The display name shown in listings and the space header. Required.
Slug The URL segment under /community/s/. Generated from the title; you can edit it. Must be unique.
Description One or two sentences shown on the space card and the space header.
Icon A visual icon shown next to the title everywhere the space appears.
Category Which top-level community category the space belongs to. Optional but recommended for navigation.
Type Forum, Q&A, Ideas, Show & Tell, or Social Feed. Cannot be changed after creation.
Visibility Public, Private, or Hidden.
Join policy Open, Approval Required, or Invite Only.
Posts per page 10, 25, or 50. Defaults to the community-wide value.

The form does its own validation in the browser before submission, then again on the server. Submitting with an empty title or a slug that already exists returns an inline error rather than a generic failure.

The Visual Icon Picker

The icon field is not a free-text field. Jetonomy ships with a Lucide icon picker so every space gets a consistent, professionally-drawn icon.

The picker shows 16 default icons up front, covering the most common community space themes: messages, lightbulb, sparkles, code, life-buoy, megaphone, palette, briefcase, gamepad, book, heart, globe, target, rocket, trophy, and users.

Click "Show more" to reveal another 8 icons for less common topics. If none of those fit, the search field at the top filters the entire Lucide catalogue by name, so typing "music" surfaces the music note icon, "camera" surfaces the camera icon, and so on.

The picker stores only the icon name, not an SVG, so the icon stays crisp at any size and automatically picks up the active theme's color tokens.

What Happens On Submit

Submitting the form does five things in one transaction:

  1. Creates the space row in wp_jt_spaces
  2. Adds the submitting user as the space admin (role = admin)
  3. Adds the space to the chosen category, if any
  4. Flushes the relevant rewrite caches so the space URL resolves immediately
  5. Redirects the user to the new space at /community/s/<slug>/

There is no approval queue. The space is live the moment the form is submitted. If your community needs an approval step before new spaces appear, file a ticket and we will surface the existing jetonomy_can_create_space filter as a setting.

Validation Hints

  • Title is required and cannot be a duplicate of an existing space title within the same category.
  • Slug is required, must be lowercase, and must be unique across the whole community. Jetonomy auto-generates a safe slug from the title; you only need to edit it if you want a specific URL.
  • Description is optional but space cards look better with one.
  • Category, Type, Visibility, and Join policy default to the community-wide defaults set in Jetonomy → Settings.
  • Posts per page defaults to the community-wide value and is read from the same setting.

Permission Gotchas

A few rules that surprise people on first use:

  • Creating is not moderating. A role granted "create spaces" is automatically space admin only for the spaces it creates. It cannot moderate other spaces it did not create.
  • Visibility is per-space, not per-role. A role allowed to create spaces can create a Hidden space. If you want to restrict that, use the jetonomy_can_create_space_with_visibility filter (developer docs).
  • Deactivating a member who created a space does not delete the space. The space remains; ownership transfers to the next admin in the space, or to the site administrator if there is no other admin.
  • Slug collisions are checked across the whole site. A member trying to create a space with a slug another space already uses will see an inline error, even if they cannot see the other space.

Front-End Form vs wp-admin

Both paths produce identical spaces. Pick whichever is faster for the situation.

Situation Use front-end Use wp-admin
You're a regular member with permission Yes Not available
You're an admin and already in the community Yes, faster Either
You're an admin setting up the community for the first time Either Either, bulk import easier
You want to create 10+ spaces in one session Either wp-admin has bulk tools
You want to seed a space with demo content wp-admin wp-admin only
You want to change advanced options (access rules, custom roles) wp-admin wp-admin only

The front-end form covers everything a member or space owner needs. The wp-admin editor adds bulk tools and a few advanced toggles that only site administrators ever touch.

Developer Hooks

Two hooks come up most often when customising this page:

  • jetonomy_use_frontend_space_edit (filter) - returns true to route both the Create and Edit space flows through the front-end UI. Default true.
  • jetonomy_can_create_space (filter) - boolean override per user. Useful if you need a per-user gate (e.g. paid membership) on top of the role-based default.

See the Developer Reference for the full signatures and examples.

What's Next?

Once a member has created a space, they often want to tweak it. The Edit Space page lets them adjust the icon, description, join policy, and more without leaving the front-end.

Edit a Space from the Front-End →

Since Jetonomy 1.4.0, space owners and moderators can edit their space directly from the front-end. They click an Edit space button in the space header and adjust the title, description, cover image, icon, type, visibility, join policy, category, posts-per-page, and prefixes without ever loading wp-admin.

What You Will Learn

  • Where the Edit space button appears and who sees it
  • Every field you can change from the front-end
  • How the cover image upload works without wp-admin permissions
  • The safety rules that prevent a space from being orphaned by accident
  • The difference between archiving and deleting a space
  • When to use the front-end editor versus the wp-admin one

Where The Edit Button Appears

The Edit space button lives in the top right of every space header, just above the post listing. It is conditional: only members with the right role inside that space see it.

The roles that see the button:

  • Space admin - the member who created the space, plus anyone they promote to admin
  • Space moderator - members promoted by a space admin
  • Site administrator - sees the button in every space

Regular members and guests never see the button. If a member loses their moderator role mid-session, the button disappears on the next page load.

The button leads to a dedicated page at /community/s/<slug>/edit/. The URL is permission-checked on every request, so typing it directly without the right role returns a 403.

What You Can Edit

Every space setting that is editable in wp-admin is editable from the front-end. The two editors are kept in lockstep release after release.

Field What it controls
Title The display name shown everywhere the space appears
Slug The URL segment under /community/s/. Changing it sets up a redirect from the old slug
Description The one-line summary shown on the space card and header
Cover image A wide banner image shown above the space header
Icon The Lucide icon shown next to the title
Category Which community category the space belongs to
Type Forum, Q&A, Ideas, Show & Tell, or Social Feed
Visibility Public, Private, or Hidden
Join policy Open, Approval Required, or Invite Only
Posts per page 10, 25, or 50
Post prefixes Optional tags shown in front of post titles (e.g. "Bug", "Idea")
Welcome message Text shown to new members the first time they enter the space

Changing the type after creation is supported but rarely a good idea. Switching a Q&A space to a Forum keeps every existing post but stops showing the "Mark as answer" affordance. The editor warns you before saving a type change.

The Visual Icon Picker

The icon picker is the same one used on the Create Space page. 16 default Lucide icons up front, 8 more behind a "Show more" button, and a search field that filters the full Lucide catalogue by name.

Changing the icon takes effect immediately on save. Cached pages and listings pick up the new icon on the next refresh. There is no manual cache flush needed.

Cover Image Upload

The cover image field accepts a JPEG, PNG, or WebP file up to 5 MB. Recommended dimensions are 1600 x 400 pixels.

Cover image upload works for any member with edit permission in the space, even if they do not have WordPress's upload_files capability. Jetonomy handles its own media path for community uploads, so granting "edit this space" does not require granting site-wide media upload rights.

Uploaded covers are stored under /wp-content/uploads/jetonomy/covers/<space-id>/. Removing or replacing a cover deletes the old file. There is a Remove cover link below the upload field that clears the image without setting a new one.

Role-Protection Rules

The front-end editor is intentionally cautious about anything that could leave a space without an owner.

  • No self-demote. A space admin cannot demote themselves if they are the only admin. The role dropdown skips the "moderator" and "member" options in that case and shows a tooltip explaining why.
  • No last-admin-out. A space admin cannot delete or archive the space if they have set themselves to leave it. The flow forces a transfer-to-another-admin step first.
  • No silent ownership transfer. Promoting another member to admin happens on the Members tab, not on the Edit space page. The editor never moves ownership in the background.

These rules exist because the most common community support ticket pre-1.4.0 was "I accidentally demoted myself and now nobody can edit the space." The front-end editor blocks that path entirely.

Archive vs Delete

The bottom of the Edit space page has two destructive actions, kept visually separate from the save button.

Archive space marks the space read-only. Existing posts and replies stay visible to anyone who could see them before. New posts and replies are disabled. The space disappears from category listings but is still reachable at its original URL. Archive is reversible: re-opening the editor and clicking Unarchive restores everything.

Use archive when:

  • A space has run its course but the content is still worth keeping
  • A seasonal event ends and you want the archive of last year's discussion to remain
  • A topic moves to a different space and you want the old discussions preserved

Delete space removes the space and every post, reply, vote, flag, and subscription in it. It cannot be undone from the UI. The button asks for explicit confirmation (typing the space title) before it fires.

Use delete when:

  • The space was created by mistake and has no real content
  • The content needs to be removed for legal or moderation reasons
  • You are cleaning up after a spam attack

If you are unsure, archive instead of delete. Archiving is reversible; deletion is not.

Front-End Edit vs wp-admin Edit

Both editors produce identical results. They share the same model, the same validation, and the same hooks.

Situation Use front-end Use wp-admin
You're a space owner without admin access Yes Not available
You're a site admin already in the community Yes, faster Either
You want to change advanced access rules Not supported wp-admin only
You want to bulk-edit multiple spaces Not supported wp-admin only
You want to change Pro extensions config Not supported wp-admin only
You want to fix a typo in the description Yes, fastest Either

The front-end editor covers the day-to-day fields a space owner touches. The wp-admin editor adds bulk tools and a handful of advanced settings that only site administrators ever need to change.

What's Next?

If you give space-creation permission to a wider group, expect those members to want a single place to see every space they run or belong to. The My Spaces page does exactly that.

My Spaces →

Discussions & Topics

Create topics, reply, vote, and manage conversations.

A topic is the primary unit of discussion in Jetonomy - every conversation, question, idea, or update starts here. This guide walks through everything that happens from the moment a member clicks "New Post" to when their topic goes live.

New post form with title, content editor, tags, and publish options

What You Will Learn

  • How to open the new post form in a space
  • Every field in the post composer and what it does
  • How Markdown formatting works in the content editor
  • How content moderation and rate limiting affect new members
  • Whether posts publish immediately or wait for approval

Opening the New Post Form

Members can start a new topic in three ways:

  1. Navigate to a space and click the New Post button in the space header.
  2. Visit the direct URL: /community/s/your-space-slug/new/
  3. Click + New from any page in the community - this opens a space picker first, then the form.

The new post form is always scoped to a specific space. If a member arrives via the generic + New button, they choose a space before the form loads. This ensures every topic lands in the right place.

The Post Composer

Title

The title field is required for Forum, Q&A, and Ideas spaces. Write a clear, specific title that tells members exactly what the topic is about before they click it.

Good: "How do I set up automatic email digests for my space?" Weak: "Help with emails"

For Q&A spaces, phrase the title as a question - it helps other members find answers when searching.

The title field is optional for Social Feed spaces, where short-form posts without titles are common.

Content

The content field supports rich text via a Markdown toolbar. You do not need to know Markdown syntax - the toolbar buttons handle formatting for you.

Toolbar options:

Button What it does
B Bold text
I Italic text
< > Inline code
Link Insert a hyperlink
Quote Block quote
Image Upload an image from your device
Code block Multi-line code with syntax highlighting

You can also type Markdown directly if you prefer:

  • **bold**bold
  • *italic*italic
  • `code`code
  • > quote → block quote

Images are uploaded to the WordPress media library. Each image is inserted as a standard <img> tag in the content. There is no separate file attachment field - all media goes inline.

Tags

Add up to five tags to help members find your topic through search and tag filtering. Type a tag name and press Enter. Jetonomy auto-suggests existing tags as you type - reusing existing tags is better than creating near-duplicates.

Tags are space-scoped by default. A "bug" tag in your Support space and a "bug" tag in your Dev space are the same tag in the database, but the tag page at /community/tag/bug/ will show posts from all spaces.

Similar Topics Detection

As you type the title, Jetonomy searches for existing topics with similar titles in the current space and shows up to five matches inline below the title field. This is the single best defense against duplicate topics - most duplicates happen because the author simply did not know an existing topic already covered their question.

If you see your question already answered in the list, click the match to jump to the existing topic instead of submitting a duplicate. If none of the matches fit, keep typing - the search re-runs after every few characters.

Similar Topics detection runs entirely on the client against the search index - no additional page load.

Prefix Selector

If the space has topic prefixes enabled, a Prefix selector appears next to the title field. Pick a prefix (for example, Bug, Suggestion, Solved) from the list and it appears as a colored label in front of your topic in the space listing.

Prefixes are configured per space by the space owner - see the Topic Prefixes guide.

Private Topic Toggle

If your space allows it, a Private toggle appears at the bottom of the composer. Enabling it restricts the topic to you and space moderators only - other space members cannot see it. Use this for sensitive support issues, personal requests, or anything that should stay between you and the moderators.

Not every space allows private topics. If you do not see the toggle, the space owner has disabled the feature.

See Private Topics and Topic Prefixes for the full guide.

Post Type Is Derived Automatically

You do not select the post type - Jetonomy determines it from the space type:

Space type Post type
Forum Discussion
Q&A Question
Ideas Idea
Social Feed Update

The composer adapts its UI accordingly. Q&A spaces show a hint to "phrase as a question." Ideas spaces show the Idea Status selector (visible to moderators). Social Feed spaces make the title optional and show a shorter, Twitter-style composer.

Content Moderation Checks

Before a post is saved, Jetonomy runs three checks:

Rate limiting - New members (Trust Level 0) can submit a maximum of 3 posts per day and 10 replies per day. If a member has hit their limit, the submit button shows an error and the post is not saved. Rate limits reset at midnight UTC. Members at Trust Level 1 and above have higher or no limits, depending on your global settings.

Require post approval - If the space has "Require Post Approval" enabled, the post is saved with a Pending status. It is not visible to other members until a moderator approves it. The submitter sees a confirmation: "Your post has been submitted and is awaiting approval."

Auto-moderation rules (Jetonomy Pro) - If Pro auto-moderation rules are configured, Jetonomy checks the content against those rules before saving. Depending on the rule configuration, the post may be flagged, held, blocked, or marked as spam automatically.

Note: WP Admins and space moderators bypass rate limiting and approval requirements. Their posts always publish immediately.

After Publishing

When a topic publishes successfully:

  • It appears at the top of the space listing under the Latest sort.
  • The author is automatically subscribed to the topic and will receive notifications for new replies.
  • The space's post count increments immediately.
  • Search indexes are updated on the next cron run (typically within a few minutes).

If the topic is pending approval, it does not appear in the listing, does not increment the post count, and the author's subscription is created but held until approval.

What's Next?

Learn how replies work, how threading is structured, and how to accept answers in Q&A spaces.

Replies & Threading →

Replies are where conversations happen. Jetonomy's reply system supports threaded discussions, multiple sort orders, accepted answers in Q&A spaces, and efficient loading for threads with hundreds of contributions.

Single topic page with threaded replies and voting controls

What You Will Learn

  • How to add a reply to a topic
  • How threaded replies work and how deep they go
  • How to sort replies and what each sort does
  • How accepted answers work in Q&A spaces
  • How to edit your own replies
  • How Jetonomy handles large threads efficiently

The Reply Composer

The reply composer appears at the bottom of every topic page. Click into the text area to expand the full Markdown toolbar - the same formatting options available in the post composer (bold, italic, inline code, links, block quotes, image upload, code blocks).

Click Reply to submit. The reply appears immediately without a page reload.

If the space has "Require Post Approval" enabled, your reply is held for moderator review before appearing to other members. A confirmation message tells you it is pending.

Quote Replies

To reply while quoting a specific passage from an existing reply, select the text you want to quote and click the Quote button that appears. Jetonomy inserts the quoted text as a styled blockquote in your reply composer, with the author attribution linked back to the source reply.

You can also click Quote in the ... menu on any reply to quote its full body without selecting text first. This is the quickest way to address a specific point from a long reply - the quoted passage gives readers the context without forcing them to scroll back up.

Threading: Replies to Replies

You can reply to any existing reply by clicking the Reply link that appears when you hover over a reply. This creates a threaded sub-reply nested visually under the parent.

Jetonomy supports three levels of nesting:

Reply (level 1)
  └─ Reply to reply (level 2)
       └─ Reply to reply to reply (level 3)

At level 3, no further nesting is allowed. Members can still reply to a level-3 comment - that reply is added at level 3 as well, keeping the conversation readable.

Threaded replies let you have multiple parallel conversations inside the same topic without them colliding. A question inside a reply gets its own sub-thread. The main discussion continues underneath.

Sorting Replies

Use the sort controls at the top of the reply list to change the order:

Oldest first - Replies appear in chronological order, oldest at the top. Best for reading a long discussion from the beginning. This is the default for Forum spaces.

Newest first - Most recent replies appear at the top. Best for active topics where you want to see the latest contributions without scrolling.

Best - Replies are ranked by net vote score (upvotes minus downvotes), highest at the top. Best for Q&A spaces or any topic where you want the most useful contributions visible first. Within the same vote score, older replies rank first.

Sort preference is stored per-session - if you change it on one topic, it persists as you navigate between topics in the same session.

Accepted Answers in Q&A Spaces

In Q&A spaces, the topic author can mark one reply as the accepted answer. This signals to other members that the question has been solved.

How to accept an answer:

  1. Open your question (you must be the post author).
  2. Find the reply that best answers your question.
  3. Click the Accept button (the checkmark icon below the reply's vote controls).
  4. The reply is immediately pinned to the top of the reply list, above all other replies and sort orders.

The accepted reply is highlighted with a green "Accepted Answer" badge. The reply author receives a notification and a +15 reputation bonus.

You can change the accepted answer at any time by clicking Accept on a different reply. The previous acceptance is removed.

Tip: Space moderators can also accept answers on any Q&A topic in their space - useful for resolving unanswered questions on behalf of absent authors.

Editing Your Own Replies

Click the ... menu on any reply you authored and select Edit. The reply text becomes an inline editor - you make changes and click Save. No page reload.

Edits are tracked as revisions internally. Moderators can view reply revision history from the moderation panel.

You can edit a reply at any time after posting. There is no edit window.

If a moderator edits your reply, the reply gets an "Edited by moderator" label.

How Jetonomy Handles Large Threads

For topics with many replies, Jetonomy uses cursor-based pagination to load replies in batches.

The first time you open a topic, you see the first batch of top-level replies (default: 20). A Load more replies button appears at the bottom if more exist. Clicking it loads the next batch without reloading the page.

For very high-traffic topics - those with hundreds of top-level replies - Jetonomy uses a smart loading strategy: it loads the first 10 replies and the last 10 replies, with a collapsed gap in the middle showing how many replies are hidden. You can click the gap to load replies from that range.

Threaded sub-replies follow the same pattern. A thread deeper than a few replies shows a "Show X more replies" link inline.

Note: New reply notifications appear as a sticky banner at the bottom of the page when other members post while you are reading. Click the banner to load the new replies without losing your scroll position.

What's Next?

Learn how upvotes and downvotes work, how they affect reputation, and how they power the Popular sort and trending sidebar.

Voting & Reputation →

Voting is the engine behind Jetonomy's quality signals. It surfaces the best content, rewards helpful members, and gives you a community where the most useful posts rise to the top naturally, without moderator intervention.

Topic page showing upvote and downvote buttons with vote scores on replies

What You Will Learn

  • How to vote on topics and replies
  • How vote scores appear in listings and on reply cards
  • How votes translate into reputation points
  • How votes power the Popular sort and trending sidebar
  • The one-vote-per-item rule and how to undo a vote

Voting on Topics and Replies

Every topic and every reply has an upvote button (thumbs up) and a downvote button (thumbs down). The net vote score (upvotes minus downvotes) is displayed between them.

To vote: Click the upvote or downvote button. The score updates instantly. No page reload.

To undo a vote: Click the same button again. The vote is removed and the score returns to its previous value.

To change your vote direction: Click the opposite button. Jetonomy removes your previous vote and applies the new one in a single action. The score adjusts correctly.

You must be logged in to vote. If you click a vote button while logged out, Jetonomy opens its in-page sign-in form (no wp-login.php bounce) and returns you to the same topic once you sign in.

Note: You cannot vote on your own posts or replies. The vote buttons are visible but disabled with a tooltip explaining why.

Note: When a space admin disables voting on a space, vote controls are removed from the interface entirely, not just greyed out. Members will not see the upvote or downvote buttons on any post or reply in that space.

Where Vote Scores Appear

Topic listing page - Each topic card shows the net vote score. This is how members quickly identify high-quality discussions without opening them.

Single topic page - The topic's vote score appears prominently alongside the title.

Reply cards - Every reply in a thread shows its vote score. In Q&A spaces, this score directly determines the Best sort order, making it easy to find the most helpful answer.

Vote scores are updated in real time as you vote. You never need to reload the page to see the current score.

How Votes Affect Reputation

When you receive votes on your content, your Jetonomy reputation score changes. Reputation is displayed on your public profile and powers your trust level progression.

Event Reputation change
Your post is upvoted +10
Your reply is upvoted +5
Your reply is accepted as an answer (Q&A) +15
Your post or reply is downvoted -2
Your post is deleted by a moderator -20

Reputation is calculated and stored in real time. The moment someone votes on your content, your reputation score updates. There is no batch processing.

Casting a downvote does not cost you any reputation points. Downvotes are free for voters.

How Votes Power Content Discovery

Votes are not just cosmetic. They feed two core discovery features:

Popular sort - On the space listing page, the Popular sort orders topics by their net vote score. High-vote topics stay visible longer. New topics start at zero and rise based on community reaction.

Trending sidebar - The community home page and individual space pages show a trending sidebar highlighting the topics with the highest recent vote velocity, meaning they received the most votes in a short window. A post with 20 upvotes in 2 hours outranks one with 100 upvotes spread over a month.

In Ideas spaces, vote scores are the primary ranking signal on the roadmap view. Ideas with the most votes appear first, giving product teams a clear priority signal.

The One Vote Per Item Rule

Each member can cast exactly one vote per post and one vote per reply. The database enforces this with a unique constraint. There is no way to vote twice on the same item.

Toggling (undoing) and changing direction both work correctly within this constraint. At any moment, your vote on a given item is either +1, -1, or nothing.

If you vote on a topic in the listing view, then open the topic and vote again, you are interacting with the same vote record. The second click undoes the first vote.

What's Next?

Learn how to bookmark topics for quick access and how to follow spaces to get notified about new posts.

Bookmarks & Following →

Bookmarks and following are how members stay connected to the content they care about. Bookmarks are a personal reading list. Following a space is a subscription to that space's activity. Together they make sure your community members never lose track of an important discussion.

Topic page with bookmark, follow, and share action buttons

What You Will Learn

  • How to bookmark a topic and access your bookmarks later
  • How to follow a space and what that means for notifications
  • How auto-subscription works when you post or reply
  • How to manage your subscriptions and turn off notifications

Bookmarking Topics

How to Bookmark

Open any topic and click the bookmark icon in the topic action bar (next to the share and report buttons). The icon fills to indicate the bookmark is saved. Click it again to remove the bookmark.

Bookmarking is instant and does not require a page reload. There is no confirmation dialog.

Accessing Your Bookmarks

Two ways to get there, both show the same list:

  • The Bookmarks tab on your profile at /community/u/your-username/
  • A dedicated standalone page at /community/bookmarks/ (new in 1.4.1)

Both list every topic you have bookmarked, most recent first. Each item shows the topic title, the space it belongs to, the author, and when it was posted. Click any item to go directly to the topic.

Bookmarks are private - other members cannot see your bookmark list. The standalone page requires sign-in and is excluded from search engines so the URL itself never reveals what you've saved.

Tip: Use bookmarks as a reading queue - bookmark topics you want to read later, then clear them as you go. The Bookmarks tab gives you a clean list without any algorithm reordering it.

Following Spaces

How to Follow

Visit any space and click the Follow button in the space header. The button changes to Following immediately.

Following a space subscribes you to new post notifications from that space. Every time a new topic is published in a space you follow, you receive a notification in your notification bell.

Unfollowing

Click Following in the space header to toggle it off. The button returns to Follow and you stop receiving new post notifications from that space.

Following a space does not make you a member of the space. In Open spaces, you can follow without joining - you will receive notifications but you will not be listed as a member and cannot post until you join.

What You Get Notified About

When you follow a space, you receive notifications for:

  • Every new topic published in that space (including by authors you do not know)

You do not receive notifications for replies to topics in followed spaces unless you are also subscribed to those individual topics. Following is space-level; subscriptions are topic-level.

Topic Auto-Subscription

When You Create a Topic

When you publish a new topic, you are automatically subscribed to it. You will receive notifications for:

  • Every new reply to your topic (including threaded sub-replies)
  • When your topic is answered and accepted (Q&A spaces)
  • When your topic's idea status changes (Ideas spaces)

When You Reply to a Topic

When you post a reply to any topic, you are automatically subscribed to that topic. You will receive the same notifications as if you had subscribed manually.

Unsubscribing from a Topic

Open the topic and click the ... menu at the top of the page, then select Unsubscribe. You will stop receiving notifications for new replies on that topic.

You can re-subscribe at any time using the same menu.

Managing All Your Subscriptions

Go to your profile's Settings page at /community/u/your-username/edit/ and open the Notifications section.

Here you can:

  • See a list of all spaces you follow with a one-click unfollow option
  • See topics you are subscribed to (the list shows the 20 most recent)
  • Set your notification preference for email delivery (immediate, daily digest, or never)

Note: Daily and weekly email digests are a Jetonomy Pro feature. In the free plugin, email notifications are sent immediately for each event or not at all - based on your notification settings.

What's Next?

Learn how to save work-in-progress posts as drafts and schedule topics for future publication.

Drafts & Scheduled Posts →

Not every post is ready to publish the moment you start writing it. Drafts let you save work-in-progress posts and come back to them later. Scheduled posts let you write now and publish at a specific date and time automatically.

Post composer with draft and schedule publishing options

What You Will Learn

  • How to save a post as a draft
  • Where to find and manage your drafts
  • How to schedule a post for future publication
  • How the draft/schedule split-button UI works
  • How scheduled posts are published and what happens if the window is missed

Saving a Draft

When you are composing a new topic and want to save your progress without publishing:

  1. Write your title and content as usual.
  2. Click the arrow next to the main Post button to expand the split-button menu.
  3. Select Save as Draft.

Your draft is saved immediately and you are returned to the space listing. No notification is sent to other members. The topic does not appear in the space listing.

Tip: You can also save a draft at any time during composition by pressing Ctrl+S (Windows/Linux) or Cmd+S (Mac). The draft saves silently in the background without closing the composer.

Finding Your Drafts

Two ways to get there, both show the same list:

  • The Drafts tab on your profile at /community/u/your-username/
  • A dedicated standalone page at /community/drafts/ (new in 1.4.1)

Your drafts are listed in order of last-modified time, most recent first. The standalone page requires sign-in and is excluded from search engines, so unfinished work never leaks. Each draft shows:

  • The post title (or "Untitled Draft" if you have not written a title yet)
  • The space it is intended for
  • When you last saved it

Click any draft to reopen the full composer with your saved content. Make your changes, then publish or save again.

Drafts are private. Only you and site admins can see your drafts.

Drafts and Post Counts

Draft posts do not count toward any community statistics until they are published:

  • They do not increment the space's post count.
  • They do not appear in the space listing.
  • They do not appear in search results.
  • They do not appear on your public profile's Posts tab.

The moment a draft is published, either manually or via scheduled publishing, all of the above update immediately.

Scheduling a Post

You can schedule a post to publish automatically at a specific future date and time:

  1. Write your title and content.
  2. Click the arrow next to the Post button to expand the split-button menu.
  3. Select Schedule.
  4. A date and time picker appears. Choose when you want the post to go live.
  5. Click Schedule Post.

The post is saved with a Scheduled status. It does not appear publicly until the scheduled time.

Scheduled posts appear in your Drafts tab with a "Scheduled" label and the publish date/time displayed. You can click into a scheduled post to edit the content or change the publish time at any point before it goes live.

To cancel a scheduled post and convert it back to a draft, open it and click Unschedule.

How Scheduled Publishing Works

Jetonomy checks for scheduled posts using WordPress Cron (WP-Cron). The check runs every hour.

This means a post scheduled for 9:00 AM may publish at any point between 9:00 AM and 10:00 AM, depending on when the next site visitor triggers a cron run.

Note: If your site uses a server-side cron (via crontab) instead of WP-Cron, scheduled posts will publish more precisely, within one minute of the scheduled time. Ask your host or developer to configure a real cron job if timing precision matters for your community.

If a post's scheduled time is missed (for example, because WP-Cron did not run during a low-traffic period), Jetonomy will publish it the next time cron runs. It never silently drops a scheduled post.

The Split-Button UI

The publish button in the post composer is a split button:

  • Left side: The primary action (defaults to Post Now for new posts, Update for existing drafts).
  • Right side (arrow): Opens the options menu with Save as Draft, Schedule, and any other contextual actions.

The left side updates based on context. When you are editing a draft, the left side shows Publish Now and the right side shows options to reschedule or keep as draft.

What's Next?

Learn the full set of moderation tools available to space moderators: moving, merging, splitting, pinning, closing, and deleting topics.

Topic Management →

Space moderators have a full toolkit for organizing, curating, and controlling discussions. Every action in this guide requires moderator permission on the space where the topic lives - or site-wide admin access.

Admin moderation panel with content review and bulk action controls

What You Will Learn

  • How to move a topic to a different space
  • How to merge duplicate topics
  • How to split a reply into a new topic
  • How to pin and unpin topics
  • How to close and reopen topics
  • How to delete topics

Who Can Use These Tools

All moderation actions described here require one of the following:

  • Space Moderator role in the space where the topic lives
  • Space Admin role in that space
  • WordPress Administrator (can moderate any space)

Regular members do not see moderation options in the menus, even if they are the topic author. Topic authors can edit and delete their own posts, but not pin, close, move, merge, or split them.

Moving a Topic to a Different Space

Use this when a topic was posted in the wrong space - for example, a bug report in a General Discussion space that belongs in a Support space.

  1. Open the topic.
  2. Click the ... menu at the top right of the topic.
  3. Select Move Topic.
  4. A modal appears with a searchable space picker. Type to filter spaces by name.
  5. Select the destination space.
  6. Click Move.

The topic disappears from the original space immediately and appears at the top of the destination space's listing. Its URL changes to reflect the new space slug. The old URL redirects to the new one automatically.

All replies, votes, bookmarks, and subscriptions move with the topic. Nothing is lost.

Tip: If you move a Q&A topic from a Q&A space into a Forum space, the accepted answer marking is preserved in the database, but the "Accepted" badge may not render in the Forum space depending on your display settings.

Merging Duplicate Topics

Use this when two members post the same question or idea in the same space. Merging moves all replies from the source topic into the target topic and deletes the source.

  1. Open the topic you want to remove (the duplicate).
  2. Click the ... menu and select Merge Topic.
  3. A modal appears with a search field. Search for the target topic by title.
  4. Select the target topic.
  5. Click Merge.

All replies from the duplicate are appended to the target topic's reply list in chronological order. The source topic is permanently deleted - it is not moved to trash. A moderator note is added to the target topic indicating that replies were merged from another topic.

Warning: Merging cannot be undone. Verify you have selected the correct target topic before confirming.

Splitting a Reply Into a New Topic

Use this when a reply inside a topic starts a new conversation that deserves its own thread.

  1. Find the reply you want to split.
  2. Click the ... menu on that reply.
  3. Select Split to New Topic.
  4. A modal appears asking for the new topic title.
  5. Enter the title and click Split.

Jetonomy creates a new topic in the same space with your chosen title. The selected reply becomes the first post content of the new topic. All sub-replies (direct children of that reply) move with it.

The original reply is removed from the source topic. A moderator note appears in the source topic: "A reply was split into a new topic: [title with link]."

Pinning and Unpinning Topics

Pinned topics appear at the top of the space listing, above all other topics, regardless of sort order. Use pinning for announcements, community rules, or important reference threads.

  1. Open the topic.
  2. Click the ... menu and select Pin Topic.

The topic moves to the top of the listing immediately and shows a pin icon to indicate its status.

To unpin, open the same menu and select Unpin Topic.

There is no limit to the number of pinned topics per space, but use pinning sparingly. Too many pinned topics reduce their signal value and push regular content below the fold.

Closing Topics

Closing a topic prevents new replies while keeping all existing content visible and readable. Use this when a question has been answered, a discussion has run its course, or a thread is becoming unproductive.

  1. Open the topic.
  2. Click the ... menu and select Close Topic.

Closed topics show a banner: "This topic is closed. No new replies can be added."

The reply composer is hidden for regular members. Moderators and admins can still reply to closed topics.

To reopen a topic, click the ... menu and select Reopen Topic.

Deleting Topics

Deleting a topic moves it to trash. It disappears from the space listing and is no longer accessible to members. The space's post count decrements.

  1. Open the topic.
  2. Click the ... menu and select Delete Topic.
  3. Confirm the deletion in the prompt.

Deleted topics can be recovered by a site admin from Jetonomy → Content in the WordPress admin - filter by status "Trash" to find them. A trashed topic can be restored or permanently deleted.

Permanent deletion removes the topic, all replies, all votes, all bookmarks, and all associated notifications from the database. It cannot be undone.

Note: When you delete a topic, the topic author's reputation is reduced by 20 points to reflect the removed content.

What's Next?

Return to the Spaces & Categories section to learn about managing space membership and access rules.

Membership & Join Policies →

Two features that give you finer control over individual topics - mark sensitive topics private so only you and moderators can see them, and use colored prefixes to classify topics at a glance in the space listing.

What You Will Learn

  • How to mark a topic as private and who can read it
  • How to enable topic prefixes for a space
  • How to create, edit, and color prefixes
  • How prefixes appear in the space listing and topic page
  • When to choose a private topic over a private space

Private Topics

A private topic is visible only to its author and to space moderators. Other members of the space cannot see it in the listing, cannot find it through search, and cannot open its URL directly - they receive a "not found" response instead.

Use private topics for:

  • Support requests that include account details, order numbers, or personal information
  • Reports of abuse or harassment where naming the other member in public would escalate the situation
  • Personal asks to a space owner that do not need a full private message thread
  • Sensitive billing or legal questions where a full support ticket is too formal

Enabling Private Topics on a Space

Private topics are an opt-in feature per space. The space owner turns them on from Jetonomy → Spaces → (your space) → Settings → Posting.

  1. Toggle Allow private topics on.
  2. Save.

If the setting is off, the Private toggle does not appear in the new post composer and members cannot create private topics in the space.

Creating a Private Topic

When the feature is enabled for a space, a Private toggle appears at the bottom of the new post composer. Turn it on before submitting and your topic is flagged as private.

Private topics show a Private badge next to the title on the topic page. The badge is visible only to you and to moderators (other members cannot see the topic at all).

Who Can See Private Topics

Role Can see private topics?
Topic author Yes
Space moderators Yes
Space owner Yes
WP admins Yes
Other space members No
Logged-out visitors No

A moderator can reply to a private topic, mark it resolved, or escalate it to a full support ticket using whatever workflow your community has. The topic author is notified of replies the same way they would be on a normal topic.

Note: Private topics are different from private spaces. A private space is hidden entirely - a member either has access to everything in it or nothing at all. A private topic is a one-off exception inside an otherwise public space. Pick private spaces for ongoing confidential work (staff lounges, paying customer lounges) and private topics for one-off sensitive conversations.

Topic Prefixes

Prefixes are colored labels that appear in front of topic titles in the space listing. They let members classify topics at a glance - Bug, Question, Solved, Announcement, Discussion, and so on.

Prefixes are configured per space by the space owner. Different spaces can have entirely different prefix sets - a support space might use Bug, Question, Solved, while a marketing space might use Idea, Campaign, Report.

Enabling and Creating Prefixes

Go to Jetonomy → Spaces → (your space) → Settings → Prefixes.

  1. Toggle Enable topic prefixes on.
  2. Click Add Prefix.
  3. Type a short label (up to 20 characters).
  4. Pick a color from the palette, or enter a custom hex value.
  5. Save.

Repeat for each prefix you want. Re-ordering a prefix in the admin changes its order in the composer picker. Prefixes you no longer need can be deleted - topics that used a deleted prefix revert to having no prefix.

Using a Prefix When Creating a Topic

When prefixes are enabled for a space, a Prefix dropdown appears next to the title field in the new post composer. Members pick one prefix per topic, or leave it blank.

If the space is configured with Require prefix enabled, the composer rejects the submission unless a prefix is selected.

Where Prefixes Appear

  • In the space listing next to the topic title
  • On the topic page, in the topic header above the title
  • In search results
  • In notification emails that reference the topic
  • In the admin moderation queue

Filter the space listing by prefix by clicking any prefix label - the listing re-renders to show only topics with that prefix.

Prefix Color Guidelines

  • Use high-contrast colors for critical prefixes (Bug in red, Announcement in blue)
  • Use muted colors for passive prefixes (Discussion in grey)
  • Avoid using green except for Solved or Done - green is a strong "resolved" signal
  • Avoid using red except for critical prefixes - red grabs attention

What's Next?

Return to Creating Topics for a full walkthrough of the new post composer, or continue to the next discussion guide.

The composer is the box you type in when you create a new post or reply. Jetonomy 1.4.0 added three features that make the composer feel more like a modern community tool and less like a comment form: @mention autocomplete, a "New" pill on unread threads, and a "Managed by" sidebar card that surfaces space staff at a glance. Admin and moderator role pills also appear next to staff names so members always know who they are talking to.

What You Will Learn

  • How @mention autocomplete works in posts and replies
  • What the "New" pill on the topic card means and when it clears
  • How the "Managed by" sidebar surfaces space admins and moderators
  • Where role pills appear and what they tell members
  • How the composer behaves with markdown vs the WYSIWYG editor

@mention Autocomplete

Type @ anywhere in the composer and Jetonomy opens a dropdown of matching people. Keep typing to narrow the list, then click a result or press Enter to insert the mention.

The inserted mention is a clickable link to the user's profile, and the moment your post or reply is published, Jetonomy fires a notification to the mentioned member. They see it in the bell menu and, if they have email notifications on, in their inbox.

Where It Works

Surface Mentions supported
New post composer Yes
Reply composer Yes
Edit post / edit reply Yes
Private message composer (Pro) Yes
Quick reply on a topic card Yes

Who Shows Up in the Dropdown

The dropdown is scoped to people the current user shares at least one space with. This keeps the list relevant on large communities and avoids leaking member names across private spaces. Within that scope, results are ordered by:

  1. Recent contributors in the current space (they are most likely to be relevant)
  2. Other members of the current space
  3. People you share other spaces with

If you start typing a name that does not match anyone in your shared spaces, the dropdown shows "No matches" rather than searching the whole site.

Walkthrough: Mentioning a Member in a Reply

  1. Open any topic and start a reply.
  2. Type @ followed by the first few letters of the person's name or username.
  3. The dropdown appears under your cursor with up to 8 matching members.
  4. Use the arrow keys to highlight a name, or click directly.
  5. Press Enter (or click) to insert the mention.
  6. Publish the reply. The mentioned member receives a notification within seconds.

Notes on Privacy

  • Mentions in a private space still notify the mentioned member, but the linked post is only visible to people who can read that space.
  • Mentioning a member who has muted you still inserts the link, but no notification is sent.
  • Site admins can disable mention notifications globally in Jetonomy → Settings → Notifications.

"New" Pill on Unread Threads

The space listing shows a card for each topic. When a topic has replies you have not read yet, a small "New" pill appears on the card.

The pill is intentionally subtle. It uses the --jt-accent design token, so it picks up your theme's brand color and matches dark mode automatically.

When the Pill Appears and Clears

Event Pill state
Someone replies to a thread you previously read Pill appears
You open the thread Pill clears immediately
You scroll past the thread without opening it Pill stays
The original poster edits their post (no new replies) Pill does not appear
You are not signed in Pill never appears (read state is per-user)

The pill is driven by the same read-status table that powers the unread indicator in the bell menu. There is no separate setting to toggle it. If you do not want unread tracking at all, disable read tracking in Jetonomy → Settings → Performance.

Mobile Behavior

On mobile the pill sits at the top-right of the card, sized for thumb readability without crowding the title. It uses the same touch target as the rest of the card, so tapping anywhere on the card opens the thread and clears the pill.

"Managed by" Sidebar Card

Every space page now shows a "Managed by" card in the sidebar. The card lists the space admin(s) and moderator(s) with their avatars and a small role badge next to each name.

What the Card Shows

  • Up to 5 staff members per space, sorted with admins first, then moderators
  • Each entry shows: avatar, display name, role badge
  • Hovering an entry reveals a "View profile" link
  • If the space has private messaging enabled (Pro), a "Message" link appears as well

If a space has more than 5 staff, the card shows the first 5 and a "See all staff" link that opens the full member list filtered to staff only.

Why This Helps Members

Members often want to ask a question, report a problem, or appeal a moderation action but don't know who to talk to. The "Managed by" card answers that question on every space page, without forcing members to dig through settings or member lists.

For owners, this surfaces your community staff and gives them visibility. Members are more likely to recognise and trust moderators they see consistently.

Customizing the Card

The card is rendered from templates/parts/space-sidebar-managed-by.php. You can override it in your theme at theme/jetonomy/parts/space-sidebar-managed-by.php to change the layout, hide certain roles, or add extra links.

Role Pills on Posts and Replies

In addition to the sidebar card, admins and moderators now have role pills next to their name on every post and reply they make. The pill is small, accessible, and uses theme tokens for color.

Role Pill label Pill style
Community Admin "Admin" Filled, accent color
Space Moderator "Mod" Outline, accent color
Member (no pill) n/a

The pills are visible to everyone, including signed-out visitors. They make it obvious when an answer comes from staff vs from another member, which matters for support spaces and Q&A spaces where members weigh staff replies more heavily.

Markdown and WYSIWYG

The composer supports two input modes, controlled per-space in Space Settings → Editor:

  • Plain text with markdown - members type using common markdown shortcuts (**bold**, *italic*, [link](url)). Lightweight, fast, ideal for technical communities.
  • WYSIWYG - a TinyMCE-style toolbar with buttons for formatting, lists, links, and image uploads. Friendlier for non-technical members.

Both modes support the same paste handling, the same image uploads, and the same @mention autocomplete. Switching modes does not lose existing content; markdown is converted to HTML and vice versa on save.

For a deeper walkthrough of editor settings see Space Settings.

What's Next?

Learn how Jetonomy's in-product modal toolkit replaces native browser dialogs with accessible, themeable confirmations.

Modals and Confirmations

Jetonomy 1.4.0 introduced an in-product modal toolkit that replaces the native browser confirm, alert, and prompt dialogs across the community. Native browser dialogs look like operating system pop-ups, cannot be themed, and don't follow modern accessibility patterns. The Jetonomy modals are themed, keyboard-accessible, and localised. The 1.4.2 retag completed full translation coverage for the button labels.

What You Will Learn

  • What the modal toolkit replaces and why
  • Where you'll see Jetonomy modals in the community
  • How the modals handle accessibility and keyboard navigation
  • How button labels translate for non-English sites
  • How dark mode and theme colors are picked up automatically
  • How to override the modal styling in your theme

What the Toolkit Replaces

JavaScript has three built-in dialog functions:

  • window.confirm("Are you sure?") returns true / false
  • window.alert("Done.") shows an info message
  • window.prompt("Reason?") asks for a text input

These look like system pop-ups. They sit outside your theme, look different on every operating system and browser, can't be styled, and don't return focus to the right element when dismissed. Screen readers treat them inconsistently, and keyboard navigation is at the mercy of the browser.

Jetonomy now uses its own modal toolkit for every "Are you sure?" prompt across the plugin. The modals look like your community, behave consistently, and play nicely with screen readers and keyboards.

Where You'll See Jetonomy Modals

Anywhere the community needs to confirm an action or collect a short answer:

Action Modal type
Delete a post or reply Confirm
Remove a flag without taking action Confirm
Ban or silence a member Prompt (asks for duration / reason)
Lift a ban Confirm
Move a topic to another space Prompt (asks for target space)
Split a reply into a new topic Prompt (asks for new topic title)
Pin or unpin a topic Confirm
Mark all notifications read Confirm
Trash a category Confirm
Approve or reject pending content Confirm
Cancel an unsaved edit Confirm

The pattern is consistent: destructive actions always require a confirmation, prompts that need an answer always show a clear input field with sensible defaults.

Modal Types

The toolkit exposes three modal types:

  • Confirm - two buttons (Cancel / Confirm). For yes/no decisions
  • Alert - one button (OK). For one-way info or success messages
  • Prompt - text input plus two buttons (Cancel / Submit). For collecting a short answer

Each type uses the same overlay and box, so the visual style is uniform across the community.

Accessibility

The toolkit is built to WCAG 2.1 AA. Every detail that screen reader users and keyboard users rely on is wired up:

Feature Behaviour
Keyboard open Focus moves to the first focusable element in the modal
Tab cycling Focus stays trapped inside the modal until it closes
Escape key Dismisses the modal, equivalent to clicking Cancel
aria-labelledby Points to the modal title element
aria-describedby Points to the modal body text
role="dialog" Set on the modal box
aria-modal="true" Tells assistive tech the rest of the page is inert
Focus return When the modal closes, focus returns to the element that opened it
Overlay click Closes the modal (configurable, off for destructive prompts)

Screen reader users hear the modal title and body when it opens. Keyboard users can tab through the controls without escaping the dialog. Mouse users can dismiss by clicking the overlay, except on destructive prompts where overlay dismiss is intentionally disabled to prevent accidental data loss.

Reduced Motion

If the visitor's operating system has "Reduce motion" enabled, the modal skips its slide-in and fade animations. The dialog still opens and closes; it just appears and disappears instead of animating. This respects vestibular accessibility preferences without compromising functionality.

Localisation

All button labels translate through the standard WordPress translation pipeline:

English label Used in
Cancel Every modal
Confirm Confirm modals
OK Alert modals
Submit Prompt modals

Modal titles and body text are localised by the calling code (e.g. "Delete this post?" is translated by the delete action that opens the modal). The toolkit itself only contributes the four button labels above.

The 1.4.2 retag completed full translation coverage for every label. Before that retag, English fallbacks could leak into translated locales for one or two of the buttons. Sites running a translated locale (French, German, Spanish, Arabic, etc.) now see fully translated buttons everywhere.

Dark Mode

The modal box and overlay both use Jetonomy design tokens. That means:

  • Dark theme active → modal renders dark automatically
  • Light theme active → modal renders light
  • Theme brand color → primary button picks it up
  • Theme border radius → modal corners match

No per-theme configuration required. If your theme defines a custom --jt-bg or --jt-accent, the modal picks it up.

Styling Override

Site owners who want a custom look can override the modal styles in their theme stylesheet. The relevant classes are:

Class Element
.jt-modal-overlay The backdrop behind the modal
.jt-modal-box The modal container
.jt-modal-title The title heading inside the modal
.jt-modal-body The body text or prompt input wrapper
.jt-modal-actions The button row at the bottom
.jt-modal-button Both buttons (Cancel / Confirm)
.jt-modal-button--primary The primary (right-hand) button
.jt-modal-button--secondary The Cancel (left-hand) button

Example: dimming the overlay further for a more cinematic feel.

.jt-modal-overlay {
  background: rgba(0, 0, 0, 0.7);
}

Example: making the modal a bit wider on desktop.

@media (min-width: 768px) {
  .jt-modal-box {
    max-width: 560px;
  }
}

We recommend overriding tokens (e.g. --jt-bg, --jt-radius) at the modal level rather than rewriting properties directly, so dark mode and theme switching keep working.

Developer Note

The toolkit exposes three global functions for any custom JavaScript that integrates with Jetonomy. Each returns a Promise.

// Yes/no confirmation
const confirmed = await window.jetonomyConfirm({
  title: 'Delete this post?',
  message: 'This cannot be undone.',
  confirmLabel: 'Delete',
  destructive: true,
});

// One-button info dialog
await window.jetonomyAlert({
  title: 'Saved',
  message: 'Your changes are live.',
});

// Text input prompt
const reason = await window.jetonomyPrompt({
  title: 'Why are you reporting this?',
  message: 'Tell our moderators what is wrong.',
  placeholder: 'Reason',
});

The destructive: true flag styles the confirm button in red and disables overlay-click dismiss. Useful for delete confirmations and account actions.

These are pure JS globals; you don't need to import anything if jetonomy is already enqueued on the page. For block development or modules, the same functions are available on window.jetonomy.modals.

What's Next?

Learn how the activity log tracks every audit-worthy event in your community.

Activity Log

Search & Discovery

Find content with full-text search, tags, and filters.

Jetonomy's search finds content across your entire community in real time - topics, replies, spaces, and tags - and lets you narrow results with powerful filters so members always land on exactly what they need.

What You Will Learn

  • How to run a full-text search from anywhere in the community
  • What the filter pills do and how to use them
  • How advanced filters refine results by date, author, tag, and sort order
  • How to show and hide the filter bar
  • How developers can extend search filters with a custom hook

Running a Search

The search bar sits in the community navigation, visible on every page. Type any keyword and press Enter or click the search icon. Jetonomy searches across:

  • Topic titles and content
  • Reply content
  • Space names and descriptions
  • Tag names

Results appear on the search results page at /community/search/. Each result card shows the content type, the space it belongs to, the author, the date, and a short excerpt with your search term highlighted.

Search results page

Tip: Phrase searches work well in Jetonomy. Wrap your query in quotes - "email digest" - to find that exact phrase rather than posts containing both words separately.

Filter Pills

At the top of the results page, four filter pills let you narrow by content type instantly:

Pill Shows
All Every matching result
Posts Topic results only
Spaces Space results only
Tags Tag results only

Click any pill to filter. The URL updates so you can share a filtered search link with your team.

Advanced Filters

Click the Filters button to expand the advanced filter bar. These filters stack - you can combine them in any combination.

Date Range

Choose a preset (Last 7 days, Last 30 days, Last year) or set a custom From / To date. Jetonomy filters by the post's original publish date, not its last reply date.

Author

Type a username to filter results to a specific author. Jetonomy auto-suggests matching members as you type. This is useful for reviewing a particular member's contributions or finding your own older posts.

Tag

Type a tag name to restrict results to posts that carry that tag. Combining author and tag filters is a fast way to find all posts by a specific member on a specific topic.

Sort Order

Option Orders by
Relevance Full-text match score (default)
Newest Most recently published first
Most Voted Highest net vote score first

Relevance is the default because it surfaces the best textual match. Switch to Newest when you know you are looking for a recent discussion. Switch to Most Voted when you want the community's highest-rated answer on a topic.

Collapsing the Filter Bar

Click Filters again to collapse the bar. Your active filters remain applied - the pill count badge on the Filters button shows how many filters are currently active.

For Developers: Extending Search Filters

The jetonomy_search_filters hook lets you add custom filter parameters to the search query. This is how Pro extensions like analytics-based filtering hook into the search pipeline.

add_filter( 'jetonomy_search_filters', function( $filters, $query_args ) {
    // Add a custom filter to restrict to a specific space.
    if ( ! empty( $_GET['space_id'] ) ) {
        $filters['space_id'] = absint( $_GET['space_id'] );
    }
    return $filters;
}, 10, 2 );

See the Hooks Reference for the full parameter list.

What's Next?

Learn how tags work across spaces and how members can browse tag pages to find related content.

Tags →

Tags connect related discussions across your entire community. A member searching for help with "payments" can click the payments tag and instantly see every relevant topic - no matter which space it lives in.

Tag page listing all topics associated with a specific tag

What You Will Learn

  • How to add tags when creating a topic
  • How tag pills work on topic listings
  • How tag pages bring together content from multiple spaces
  • How the Popular Tags sidebar widget helps members discover content
  • Why consistent tagging improves search and community quality

Adding Tags to a Topic

When creating or editing a topic, the Tags field appears below the content editor. Type a tag name and press Enter or comma to add it. You can add up to five tags per topic.

As you type, Jetonomy shows a live dropdown of existing tags that match your input. Select an existing tag whenever possible - reusing tags makes the tag page more useful for everyone who follows that topic.

To remove a tag, click the X on its pill in the field.

Tip: In Q&A spaces, good tags are the fastest route to an answer. A question tagged with "payments" and "stripe" will appear on both tag pages, doubling its chance of being seen by someone who can help.

Tag Pills on Topic Listings

Every topic card in a space listing shows its tags as small pills below the title. Clicking a tag pill takes you directly to that tag's page - you do not leave the flow to reach it.

Tags in listings are a fast way to browse by topic without running a search. If a space covers multiple subjects, tag pills help members navigate without scrolling through unrelated posts.

Tag Pages

Every tag has a dedicated page at /community/tag/tag-slug/. The tag page shows all published topics across all spaces that carry that tag, sorted by newest by default.

Members can switch the sort order to Most Voted to find the best content on that topic, or use the date filter to narrow to recent discussions.

Tag pages are publicly accessible by default. If your community is private, tag pages respect the space access rules - topics in private spaces are not shown on tag pages to members who do not have access to those spaces.

Popular Tags Sidebar Widget

The Popular Tags widget lists the tags used most frequently across your community. Add it to your sidebar from Appearance → Widgets and select the Jetonomy: Popular Tags widget.

Configuration options:

Option Default Description
Title Popular Tags Widget heading text
Count 10 Number of tags to display
Show count Yes Display the number of posts per tag

The widget refreshes its data hourly via WordPress transients so it does not run a query on every page load.

How Tags Improve Your Community

Tags work across space boundaries. A tag named "onboarding" can tie together a tutorial in your Guides space, a question in your Q&A space, and a feature idea in your Ideas space. Members following that tag see the full picture regardless of which space each post lives in.

Encouraging consistent tag use - especially in high-traffic spaces - pays dividends in search quality. Jetonomy's full-text search treats tags as a search signal, so well-tagged posts surface higher in relevant queries.

Note: Tags are shared across all spaces. There is one global tag namespace. A tag created in your Support space appears on the same tag page as the same tag used in your Ideas space.

What's Next?

Learn how Jetonomy's trust system automatically identifies your most reliable members and gives them more capabilities over time.

Trust Levels & Reputation →

Moderation & Trust

Keep your community safe with trust levels and moderation tools.

Jetonomy's trust system automatically promotes reliable members to higher privilege levels as they earn reputation - so you spend less time manually managing who can do what, and your most active members get recognized for their contributions.

Permissions settings with trust level thresholds and promotion rules

What You Will Learn

  • What the six trust levels are and what each one unlocks
  • How members earn reputation points
  • How automatic promotion works
  • How to adjust thresholds in your admin settings
  • How trust badges appear on member avatars

The Six Trust Levels

Jetonomy has six trust levels, numbered 0 through 5. Every new member starts at Trust Level 0 and rises as they earn reputation and meet the configured thresholds.

Level Name Default Threshold
TL0 New Member Automatic on signup
TL1 Basic Member 50 reputation
TL2 Member 200 reputation
TL3 Regular 500 reputation
TL4 Leader 1,000 reputation
TL5 Trusted 2,500 reputation

What Each Level Unlocks

Trust levels expand what a member can do without moderator intervention.

Capability TL0 TL1 TL2 TL3 TL4 TL5
Create topics Yes Yes Yes Yes Yes Yes
Post replies Yes Yes Yes Yes Yes Yes
Upload images No Yes Yes Yes Yes Yes
Flag content Yes Yes Yes Yes Yes Yes
Skip CAPTCHA No No Yes Yes Yes Yes
Edit own posts Yes Yes Yes Yes Yes Yes
Daily post limit lifted No No Yes Yes Yes Yes
Rate limit lifted No No Yes Yes Yes Yes

Space moderators and WordPress admins always have full capabilities regardless of trust level.

How Members Earn Reputation

Reputation is updated in real time whenever a qualifying event occurs.

Event Points
Your topic is upvoted +10
Your reply is upvoted +5
Your reply is accepted as an answer (Q&A) +15
Your topic or reply is downvoted -2
A moderator deletes your content -20

Reputation points accumulate on your public profile. The leaderboard ranks members by reputation score - see the Leaderboard doc for details.

Automatic Promotion

A cron job runs twice daily to evaluate all members against the current trust level thresholds. Any member whose reputation meets or exceeds the next threshold is automatically promoted.

Promotion is silent - members are not notified by default. You can add a welcome notification using the jetonomy_trust_level_changed action hook if you want to acknowledge promotions.

Demotion works the same way. If a member's reputation falls below a threshold (for example, because posts were deleted), they are automatically moved back to the appropriate level on the next cron run.

Tip: You can trigger an immediate re-evaluation for a specific user from Jetonomy → Users in the WordPress admin. Find the user and click Recalculate Trust Level.

Configuring Thresholds

Go to Jetonomy → Settings → Permissions to adjust the reputation threshold for each trust level. Changes take effect on the next cron run - or immediately if you run a manual recalculation.

Lower thresholds make promotion faster and more accessible. Higher thresholds make higher trust levels a meaningful achievement. There is no right answer - tune these to the pace and size of your community.

Note: Setting a threshold to 0 makes that trust level automatic for all members who meet all lower thresholds. Use this carefully - it effectively grants TL capabilities to your entire community.

Trust Badges on Avatars

Each trust level has a colored badge that appears on a member's avatar across topic listings, reply cards, and their profile page. The badge uses the data-jt-tl attribute so you can restyle it in your theme using CSS if needed.

Level Badge Color
TL0 Grey
TL1 Blue
TL2 Green
TL3 Teal
TL4 Purple
TL5 Gold

Why Trust-Based Moderation Beats Manual Role Assignment

In a traditional forum, you manually decide who is a "trusted" member. That does not scale. With Jetonomy's trust system, your community self-selects. Members who contribute quality content earn their way to higher levels automatically. You only need to intervene in edge cases - banning bad actors or manually elevating a known expert to a higher level.

What's Next?

Learn how members can flag content for review and how flagged content reaches the moderation queue.

Flagging & Reporting Content →

Flagging lets any logged-in member report content that breaks your community rules. It is the first step in the moderation pipeline - members surface problems, and your moderators review and act.

Admin moderation dashboard showing flagged content awaiting review

What You Will Learn

  • How to flag a topic or reply
  • What information a flag captures
  • Who can flag content and what the restrictions are
  • What happens to flagged content before a moderator reviews it
  • How flags flow into the moderation queue

How to Flag Content

Every topic and every reply has a ... (more actions) menu. Open it and click Report. A small modal appears asking for a reason. Type a brief description of the problem - for example, "This contains spam links" or "This is abusive toward another member" - and click Submit Report.

The flag is saved immediately. You receive a confirmation message and the modal closes.

Tip: A good flag reason helps moderators act faster. "Spam" alone works, but "Contains a link to a commercial site unrelated to this community" gives the moderator everything they need without having to investigate.

Who Can Flag Content

Any logged-in member can flag a topic or reply, regardless of their trust level. There is one restriction: you cannot flag your own content.

The flag button is not visible to guests (logged-out visitors). If you want guests to be able to report content, you will need a custom solution - the built-in flagging system requires authentication.

There is no daily limit on flags per member. A member who finds multiple pieces of problematic content can flag all of them.

What a Flag Captures

When a flag is submitted, Jetonomy records:

  • The content being flagged (post or reply, with its ID)
  • The member who submitted the flag
  • The reason text they entered
  • The timestamp

Moderators see all of this information when they review the flag in the moderation queue.

What Happens to Flagged Content

Flagging alone does not change the visibility of a post or reply. The content stays live and readable by all members until a moderator reviews it and takes action. This is intentional - hiding content automatically on a single flag would allow abuse of the flagging system.

A moderator can then approve the flag (confirming the content breaks the rules) and take action, or dismiss the flag (marking it unfounded). See the Moderation Queue guide for the full review workflow.

Note: If the same piece of content receives multiple flags from different members, all flags are grouped under that content item in the moderation queue. Moderators see the total flag count and each individual reason.

Preventing Flag Abuse

Jetonomy does not currently auto-penalize members who submit flags that moderators consistently dismiss. If you have a member who is abusing the flagging system, handle it by adjusting their trust level or banning them from Jetonomy → Users in the WordPress admin.

What's Next?

See how flagged content and posts pending approval appear in the moderation queue, and learn how moderators take action.

Moderation Queue →

The moderation queue is your single dashboard for everything that needs human review - posts waiting for approval, flagged content, and items caught by spam filters. You can action everything from one page without digging through individual topics.

Admin moderation queue with pending items and bulk action controls

What You Will Learn

  • How to access the moderation queue
  • What types of content appear in the queue
  • What actions you can take on each item
  • How to use bulk actions for efficiency
  • How per-space moderation differs from global moderation
  • How Akismet-held content appears in the queue

Accessing the Moderation Queue

Go to Jetonomy → Moderation in your WordPress admin. The page is also accessible from the frontend at /community/mod/ - WordPress Administrators and users with the jetonomy_moderate capability can access both routes.

The queue shows a count badge on the admin menu item whenever there are items waiting for review.

What Appears in the Queue

The queue has two sections:

Pending Posts

These are topics and replies that were submitted in a space with Require Post Approval enabled. They are not visible to other community members until a moderator approves them.

Each pending item shows the full content, the author, the space it was submitted to, and how long it has been waiting. Items are ordered oldest first so nothing sits in the queue unnoticed.

Flagged Content

These are live topics and replies that members have flagged for review. Flagged content stays visible in the community until a moderator acts. Each item shows the content, the flag reason(s), how many unique members flagged it, and the timestamp of the most recent flag.

Available Actions

For each item in the queue, you can take one of three actions:

Action What it does
Approve Publishes a pending post, or resolves a flag as "Valid" and leaves the content live
Mark as Spam Marks the content as spam and moves it to trash; updates Akismet's spam training if Akismet is active
Trash Moves the content to trash without marking it as spam

For flagged content, Approve resolves the flag as dismissed - meaning the flag was unfounded and the content is fine. The content stays live. Use Trash or Mark as Spam to remove content where the flag was legitimate.

Tip: Use Mark as Spam rather than Trash when content is clearly commercial spam. This trains Akismet for your site, making future auto-detection more accurate.

Bulk Actions

Check the checkbox on multiple queue items, then choose an action from the Bulk Action dropdown and click Apply. All three actions - Approve, Mark as Spam, Trash - are available as bulk actions.

Bulk actions are the fastest way to clear a backlog. If you have 40 flagged items from a spam attack, select all and bulk-trash them in one click.

Per-Space vs Global Moderation

The queue shows content from all spaces by default. Use the Space filter dropdown at the top of the queue to narrow to a single space. This is useful when you have dedicated space moderators - a moderator for your Support space only needs to see Support space items.

Space moderators who do not have global admin access see only their own spaces' items when they visit /community/mod/. They do not see content from spaces they do not moderate.

Fixed in 1.4.1: moderators of multiple spaces now see every queue they own when they visit /community/mod/. Earlier versions could redirect a multi-space moderator away from the dashboard if access checks ran in the wrong order. If you have moderators who report "I can see one space's queue but not the others," update to 1.4.1 and the dashboard will load all of them.

Akismet Integration

If the Akismet Anti-Spam plugin is active and configured on your site, Jetonomy automatically passes new posts and replies through Akismet before saving them. If Akismet marks content as spam:

  • The post or reply is saved with a Spam status (not Pending)
  • It does not appear in the community
  • It appears in the moderation queue under a "Spam" filter tab

You can review Akismet-held items and restore any that were incorrectly caught by clicking Not Spam. This action publishes the content and updates Akismet's model.

Note: Akismet integration requires the Akismet plugin to be installed, activated, and connected with a valid API key. Jetonomy does not bundle Akismet - it integrates with it automatically when present.

What's Next?

Learn about Jetonomy's built-in anti-spam tools - reCAPTCHA, Turnstile, and rate limiting - that reduce how much reaches the moderation queue in the first place.

Anti-Spam Protection →

Spam is the fastest way to kill a community's quality. Jetonomy has multiple layers of protection that work silently in the background - your real members never know they are there.

Anti-spam settings with CAPTCHA provider selection and API key fields

What You Will Learn

  • How to enable reCAPTCHA v3 or Cloudflare Turnstile
  • Where to enter your API keys in the admin settings
  • How trusted members are automatically exempted from CAPTCHA
  • How Akismet integration catches spam that passes CAPTCHA
  • How rate limiting protects against new-member spam floods

Choosing a CAPTCHA Provider

Go to Jetonomy → Settings → Anti-Spam to configure your CAPTCHA provider.

Jetonomy supports two providers:

Provider Type User friction
Google reCAPTCHA v3 Score-based, invisible None - runs in background
Cloudflare Turnstile Challenge-based, invisible None - usually auto-passes

Both are invisible to real users. reCAPTCHA v3 assigns a bot-likelihood score and blocks low-scoring submissions. Cloudflare Turnstile analyzes browser signals and shows a challenge only when it cannot verify the user automatically.

Choose Cloudflare Turnstile if your community has members who are privacy-conscious or who block Google services. Both providers have free tiers sufficient for most communities.

Setting Up reCAPTCHA v3

  1. Go to google.com/recaptcha and create a v3 site.
  2. Add your domain to the allowed domains list.
  3. Copy the Site Key and Secret Key.
  4. In Jetonomy → Settings → Anti-Spam, select reCAPTCHA v3, paste both keys, and click Save.

Setting Up Cloudflare Turnstile

  1. Log in to your Cloudflare dashboard and go to Turnstile.
  2. Add a site and set the widget mode to Invisible.
  3. Copy the Site Key and Secret Key.
  4. In Jetonomy → Settings → Anti-Spam, select Cloudflare Turnstile, paste both keys, and click Save.

Note: After saving, Jetonomy automatically loads the CAPTCHA script on the post and reply forms. You do not need to add any code to your theme.

Trusted Members Skip CAPTCHA

Members at Trust Level 2 and above never see or trigger a CAPTCHA check. Jetonomy bypasses the verification entirely for them - the API call is never made.

This means your most active, trusted members post without any friction while new accounts get verified. As members earn reputation and cross the TL2 threshold, they transition out of CAPTCHA checks automatically.

Akismet Integration

If the Akismet plugin is active on your site, Jetonomy sends every new post and reply through Akismet's spam detection API as a second layer of protection. Content that passes CAPTCHA but that Akismet marks as spam is held in the moderation queue rather than published.

See the Moderation Queue guide for how to review Akismet-held content.

Akismet and CAPTCHA work independently. You can run both at the same time for maximum protection, or use either one alone.

AI-Powered Spam Detection (Pro)

Jetonomy Pro's AI extension adds a third layer: a language model reads every new post and reply and scores it for spam probability before it is published. Posts above the configured threshold go to the moderation queue with a reason the model generated. Posts below it are published as normal.

This catches the kinds of spam that pattern matching and Akismet miss - subtly rewritten affiliate spam, contextually wrong replies designed to pad posting history, and coordinated attacks that use clean accounts.

Run the detection through any supported provider - OpenAI, Anthropic, a custom endpoint, or self-hosted Ollama (which keeps all content on your own server). See AI Integration for setup.

AI spam detection stacks on top of CAPTCHA, Akismet, and rate limits - not instead of them. Each layer catches different threats.

Rate Limiting for New Members

Trust Level 0 members (brand-new accounts) are subject to posting rate limits regardless of CAPTCHA:

Content type Default limit
Topics per day 3
Replies per day 10

Limits reset after 24 hours. Members at Trust Level 1 and above are exempt from all rate limits.

You can adjust the default thresholds at Jetonomy → Settings → Permissions. Setting a limit to 0 disables it for that trust level.

Tip: Rate limiting is your best defense against coordinated spam from many new accounts. Even if a bot farm passes CAPTCHA, each account can only post 3 topics before hitting the daily limit.

What's Next?

Learn how in-app notifications keep your members engaged and informed about replies, mentions, and votes.

Notifications →

Notifications & Email

Stay informed with in-app and email notifications.

Notifications keep your community members in the loop without requiring them to check back manually. Every relevant activity (replies, mentions, votes) surfaces instantly in the notification bell so members always know when something needs their attention.

Notifications panel showing recent activity alerts and unread count

What You Will Learn

  • How the notification bell works and where it appears
  • Every notification type and when each one fires
  • How to mark notifications as read
  • How to view the full notifications history
  • Where members set their personal notification preferences

The Notification Bell

The notification bell icon appears in the community navigation bar on every page. When you have unread notifications, a red badge shows the count. The count updates automatically. You do not need to refresh the page.

Click the bell to open a dropdown showing your most recent notifications. The dropdown lazy-loads its content when you click, keeping page load times fast.

Each notification in the dropdown shows:

  • The notification type (icon)
  • A summary of what happened (for example, "Sarah replied to your topic")
  • The time it occurred
  • A direct link to the relevant content

Clicking a notification in the dropdown marks it as read and navigates you to the relevant topic, reply, or profile.

Notification Types

Jetonomy fires a notification for each of the following events:

Event Who receives it Channels
Someone replies to your topic Topic author In-app, email
Someone replies to your reply (threaded) Reply author In-app, email
Someone mentions you with @username Mentioned member In-app, email
Your reply is accepted as an answer (Q&A) Reply author In-app, email
A new topic is posted in a space you follow All followers of that space In-app, email
Your topic or reply receives an upvote Content author In-app
Your topic or reply receives a downvote Content author In-app
An idea's status changes (Ideas spaces) Idea author + all followers of that space Activity log, email digest, in-app inbox

Upvote and downvote notifications can be turned off per-member if members prefer not to see them. See the Preferences section below.

Marking Notifications as Read

Mark one as read: Click any notification in the dropdown. Navigating to it marks it read automatically.

Mark all as read: Click the Mark all as read link at the top of the dropdown. All notifications are cleared in a single action. The badge disappears immediately.

Unread notifications are highlighted with a subtle background tint in the dropdown so you can spot them at a glance.

The Full Notifications Page

The dropdown shows your most recent notifications, roughly the last 10 to 20 items depending on screen size. To see your full notification history, click See all notifications at the bottom of the dropdown or navigate directly to /community/notifications/.

The full page lists every notification you have received, paginated in groups of 25. You can filter by read / unread status. Notifications older than 90 days are automatically cleaned up from the database by a background cron job.

Per-User Notification Preferences

Each member can control which notification types they receive. Go to Profile → Edit Profile → Notifications (at /community/u/your-username/edit/).

Options are:

  • In-app notifications on/off per type
  • Email notifications on/off per type (see Email Notifications for the full email guide)

Members cannot disable notifications for direct mentions. The @mention notification is always delivered to ensure important communications reach their target.

Note: Administrators can set the default notification preferences that new members start with. Go to Jetonomy → Settings → Email to configure the defaults applied at signup.

What's Next?

Learn how to configure email notification delivery, set community-wide defaults, and understand which notification types support email.

Email Notifications →

Email notifications bring members back to your community even when they are not actively browsing. Jetonomy sends a notification email for every in-app event type, and both you and your members have control over which emails are delivered.

Admin email settings with notification toggles and sender configuration

What You Will Learn

  • Which notification types send email
  • How to configure default email settings for new members
  • How members control their own email preferences
  • How one-click unsubscribe links work
  • How Jetonomy sends email and how to use your own SMTP provider
  • What email digest options are available in Jetonomy Pro

Which Notification Types Send Email

Every notification type that appears in the in-app bell can also send an email. The email mirrors the in-app notification - it names the event, shows a short excerpt of the content, and includes a direct link back to the relevant topic or reply.

Notification type Email sent by default
Reply to your topic Yes
Reply to your reply Yes
@mention Yes
Answer accepted (Q&A) Yes
New post in followed space No
Upvote on your content No
Downvote on your content No

The defaults above are what Jetonomy applies when a new member signs up. You can change these defaults in Jetonomy → Settings → Email.

Tip: "New post in followed space" email is off by default because members who follow many active spaces would receive a high volume of email. Let members opt in rather than having to opt out.

Editable Email Templates (updated in 1.4.1)

Every notification email Jetonomy sends has an editable template - subject and body - at Jetonomy → Settings → Email → Templates. Change the wording to match your community's voice without writing any code.

Two improvements landed in 1.4.1 that are worth knowing about:

  • Reset to default button on every template - one click restores the shipped subject and body. No more retyping if you change your mind or want to start over.
  • Verification reminder template is now editable from the same screen. The reminder fires once per member, 24 hours after sign-up, if they haven't clicked the verification link in their welcome email. The interval is configurable.

Defaults now have a single source of truth so reset always restores the exact copy the plugin ships with - even if a future update changes the default wording, your reset still gets the version you'd see on a fresh install.

Configuring Default Settings

Go to Jetonomy → Settings → Email to set the community-wide defaults.

Setting Default Description
Email notifications enabled Yes Master switch - turns off all notification email for the site
Default: reply notifications Yes Whether new members receive reply emails by default
Default: mention notifications Yes Whether new members receive mention emails by default
Default: followed space notifications No Whether new members receive followed-space emails by default
Default: vote notifications No Whether new members receive vote emails by default
From name Your site name The sender name that appears in email clients
From email WordPress admin email The sender address for notification emails

Changes to defaults apply only to new members who sign up after the change. Existing members keep their current preferences.

How Members Control Their Preferences

Each member can override the defaults from their profile settings at /community/u/their-username/edit/ under the Notifications tab. Every notification type has a separate toggle for in-app and email delivery.

Members can disable all notification emails at once with the Pause all email toggle. This is the equivalent of a temporary snooze - all preferences are preserved so they can turn email back on later.

One-Click Unsubscribe

Every notification email sent by Jetonomy includes an unsubscribe link in the footer. Clicking it takes the member to a confirmation page where they can unsubscribe from that specific notification type or from all notification emails in one click.

No login is required to unsubscribe - the link contains a signed token that authenticates the action. This keeps unsubscribe rates low and means members who receive an email do not need to remember their password to opt out.

How Jetonomy Sends Email

Jetonomy uses WordPress's wp_mail() function to send all notification emails. This means it automatically works with any SMTP plugin you have installed - WP Mail SMTP, FluentSMTP, Postmark, Mailgun, or any other wp_mail compatible provider.

No additional configuration is needed in Jetonomy when you add an SMTP plugin - it just works.

Note: If you send high volumes of notification email, use a dedicated transactional email provider (Postmark, Mailgun, Sendgrid) rather than your hosting server's mail. Dedicated providers give you delivery tracking, bounce handling, and higher sending limits.

Email Digest (Jetonomy Pro)

PRO - This feature requires Jetonomy Pro.

The Email Digest feature bundles all notifications from a configurable time window into a single summary email. Members can choose daily or weekly digests instead of receiving individual emails for each event. This dramatically reduces email volume for active community members while keeping them informed.

What's Next?

Learn how member profiles work, what stats are displayed, and how members can edit their own information.

User Profiles →

User Profiles & Leaderboard

Member profiles, reputation, and community rankings.

Every member in your community has a public profile page that shows who they are, how much they contribute, and what they have been up to. Profiles build trust between members and give contributors the recognition they have earned.

What You Will Learn

  • What a member profile page shows
  • How online status works on profiles
  • Which tabs are available and who can see each one
  • How members edit their own profile
  • Where reputation and trust level appear

The Profile Page

Every member has a profile page at /community/u/their-username/. Anyone can visit this page - no login is required unless your community is set to private.

User profile page

Header Section

The profile header shows:

  • Avatar - pulled from WordPress Gravatar by default. Members can upload a custom avatar if your theme supports it.
  • Display name - the name shown across all community activity
  • Username - the login handle used in @mentions and the profile URL
  • Bio - a short text description the member writes themselves
  • Join date - when the member's account was created
  • Online status - a green dot appears if the member was active in the last 5 minutes (see Online Status)

Stats Bar

Below the header, four stats appear at a glance:

Stat What it shows
Reputation Total reputation score earned
Posts Number of published topics
Replies Number of published replies
Trust Level Current trust level badge and name

The reputation score links to the leaderboard. The trust level badge uses the same color coding as it does on reply cards - grey for TL0, scaling up to gold for TL5.

Profile Tabs

Posts

Lists every topic the member has published, paginated 20 per page, sorted newest first. Each item shows the topic title, the space it belongs to, the vote score, and the reply count.

Replies

Lists every reply the member has posted across all spaces, paginated 20 per page, newest first. Each item shows the reply excerpt, the topic it belongs to, and the vote score.

Votes

Shows the content this member has voted on. The tab is visible to the member themselves and to WordPress Administrators. Other members cannot see this tab - voting is semi-private.

Drafts

Shows unpublished draft topics saved by this member. This tab is visible only to the profile owner and WordPress Administrators. Drafts are never exposed publicly.

Tip: Encourage active members to complete their bio. Profiles with a bio and a recognizable avatar get more replies - other members feel more comfortable engaging when they know who they are talking to.

Editing a Profile

Members can edit their own profile at /community/u/their-username/edit/. This page is accessible from the Edit Profile button on the profile page header.

Editable fields:

  • Display name
  • Bio
  • Notification preferences (email and in-app toggles per type)

WordPress Administrators can edit any member's profile from the standard WordPress Users admin as well.

What's Next?

See how your top contributors rank against each other on the community leaderboard.

Leaderboard →

The leaderboard turns quality participation into something visible and worth competing for. Your top contributors are recognized publicly, which encourages every member to engage more thoughtfully.

What You Will Learn

  • How to find the leaderboard page
  • What information is displayed for each member
  • How the top 3 medal positions work
  • How to add the leaderboard sidebar widget
  • Why public recognition improves community quality

The Leaderboard Page

The community leaderboard is available at /community/leaderboard/. Any member - and guests, if your community is public - can view it. No login is required.

Leaderboard page

Members are ranked by total reputation score, highest first. The leaderboard updates in real time as reputation changes - there is no daily cache delay between earning reputation and appearing in the rankings.

What Each Row Shows

Every row on the leaderboard displays:

Column Description
Rank Position number (1, 2, 3...)
Avatar Member's profile picture with online status dot
Name Display name, linked to their profile page
Trust badge Colored trust level badge
Reputation Total reputation score
Posts Total published topic count

Clicking a member's name or avatar goes directly to their profile page.

Top 3 Medal Positions

The first three positions on the leaderboard display a medal icon next to the rank number:

Position Medal
1st place Gold medal
2nd place Silver medal
3rd place Bronze medal

Medal icons draw the eye immediately when someone opens the leaderboard page. Being in the top 3 is a genuine achievement that members notice and compete for.

The Leaderboard Sidebar Widget

Add the Jetonomy: Top Members widget to any sidebar from Appearance → Widgets. The widget shows the top 5 members by reputation with their avatars and scores - a compact preview of the leaderboard without requiring members to visit the full page.

Configuration options:

Option Default Description
Title Top Members Widget heading text
Count 5 Number of members to show (max 10)

The widget data is cached for 5 minutes to keep database queries low on high-traffic sites.

Why the Leaderboard Improves Community Quality

Recognition is a powerful motivator. When members see their name on the leaderboard, they are more likely to write detailed answers, respond helpfully to new members, and keep coming back. Members who are close to moving up a rank are especially motivated - the leaderboard creates natural competition without requiring badges, gamification plugins, or manual awards.

Tip: Reference the leaderboard in your community welcome message or newsletter. "See who our top contributors are this month" gives members a reason to engage and something to aspire to.

What's Next?

Learn how the online status green dot works - when it shows, where it appears, and how Jetonomy tracks it efficiently.

Online Status →

The online status green dot shows which members are currently active in your community. It makes conversations feel more live and helps members know when is a good time to expect a quick reply.

User profile page showing avatar with online status indicator

What You Will Learn

  • When the green dot appears on a member's avatar
  • Where online status is displayed across the community
  • Where it is intentionally not shown and why
  • How Jetonomy tracks activity without overloading the database
  • That no setup is needed - it works automatically

When the Green Dot Appears

A green dot appears on a member's avatar when they have been active in the community within the last 5 minutes. Active means any authenticated page load or interaction - visiting a topic, posting a reply, casting a vote, or navigating between pages.

After 5 minutes of inactivity, the dot disappears automatically on the next page load that renders it.

There is no manual "appear offline" or "appear online" toggle. Online status reflects real activity.

Where Online Status Appears

The green dot is shown in these locations:

Location Shown
Reply cards (author avatar) Yes
Member profile page (header avatar) Yes
Sidebar Top Members widget Yes
Topic listing rows (author credit) No
Search results (author credit) No

Why Not on Topic Listing Rows

Topic listing pages can show dozens of topics, each by a different author. Rendering the online status for every author on that page would require checking multiple records in a single request - potentially 20 to 50 lookups per page view on an active community.

Jetonomy deliberately omits the online dot from listing rows to keep those pages fast. The detail-level contexts (reply cards, profiles, widgets) are the right place for presence information because you are already focused on a specific member.

How Activity Tracking Works

When a logged-in member loads any community page, Jetonomy records a timestamp on their user record. To prevent a database write on every single page view, updates are rate-limited: Jetonomy writes the timestamp at most once per minute per member.

If a member loads 10 pages in 30 seconds, Jetonomy writes to the database once. This keeps the wp_jt_user_profiles table write volume proportional to the number of unique active members, not the total number of page views.

The online status check itself (reading whether a member is active) is cached for 60 seconds using WordPress transients. On a page with 10 reply cards by different authors, Jetonomy makes at most a small batch of cache reads rather than 10 individual queries.

No Setup Required

Online status is automatic. There is no setting to enable, no API key to configure, and no JavaScript polling. It works the moment Jetonomy is activated.

Note: Online status is only tracked for logged-in members. Guests who browse your community are not tracked and do not show an online indicator.

What's Next?

Go back to explore other sections, or return to the main community setup guide.

User Profiles → | Leaderboard →

Since Jetonomy 1.4.0, every signed-in member has a personal page at /community/my-spaces/ that lists every space they run and every space they're a member of, in one place. It is the fastest way to jump back into a space you are active in without scrolling the home page.

What You Will Learn

  • Where the My Spaces page lives and how to reach it
  • The two sections shown on the page and what they mean
  • What each row tells you at a glance
  • Quick actions available per row
  • What members see when they're brand new and have not joined any space yet
  • The privacy semantics: who can see this page

Where The Page Lives

The page is always at /community/my-spaces/. It is signed-in only. Visiting the URL while signed out redirects to the login page and returns to My Spaces after a successful sign-in.

There are three built-in ways to reach the page:

  • The My Spaces link in the header avatar menu (added automatically in Jetonomy 1.4.0+)
  • The My Spaces tab on /community/u/<your-login>/ (your own profile page)
  • The mobile drawer menu under "Community"

If your theme overrides the header or profile templates, the link may not appear automatically. The page itself still works at the URL.

The Two Sections

The page is split into two sections, stacked top to bottom.

Spaces You Run

The first section lists every space where you are a space admin or space moderator. These are the spaces you have authority over.

For each space, the row shows:

  • The space icon and title
  • A role badge ("Admin" or "Moderator")
  • The current unread count
  • The last activity timestamp ("Active 2 hours ago")
  • Quick action buttons: Visit, Edit, Members

If you run no spaces, this section is replaced with a short empty state: "You don't run any spaces yet. If your community allows it, you can start one." If front-end space creation is enabled for your role, the empty state includes a Create a space button that goes to /community/new-space/.

Spaces You're In

The second section lists every space where you are a regular member. These are the spaces you have joined but do not moderate.

For each space, the row shows:

  • The space icon and title
  • The space type (Forum, Q&A, Ideas, Show & Tell, Social Feed)
  • The current unread count
  • The last activity timestamp
  • Quick action buttons: Visit, Leave

If you have not joined any spaces yet, this section shows a friendly empty state with a Browse the community button that goes to the community home.

What Each Row Tells You

The row layout is designed to answer two questions at a glance: "Is there anything new to read here?" and "What can I do here right now?"

Element What it means
Icon The Lucide icon picked by the space owner
Title The space name, linked to the space home
Role badge "Admin", "Moderator", or no badge for regular members
Unread count New posts and replies you have not read yet
Last activity When the most recent post or reply landed in the space
Type label Forum, Q&A, Ideas, Show & Tell, or Social Feed

The unread count is per-space, computed from your last-read timestamp. Catching up on a space marks it read and the count goes to zero until new content arrives.

Quick Actions

Each row carries one to three buttons depending on your role in the space.

  • Visit is always present. It opens the space home.
  • Edit appears only on rows in the "Spaces you run" section. It opens the front-end Edit Space page covered in the previous article.
  • Members appears only on rows in the "Spaces you run" section. It opens the members tab where you can promote, demote, or remove members.
  • Leave appears only on rows in the "Spaces you're in" section. Clicking it asks for confirmation and then removes you from the space.

A space admin who is the only admin cannot leave their own space. The Leave button is hidden in that case and a tooltip explains that ownership must be transferred first.

Empty States

Brand-new members often land on My Spaces before they have joined anything. The page handles four empty-state combinations:

You run You're in What the page shows
Nothing Nothing A single full-page empty state inviting you to browse the community
Nothing One or more Section 1 collapsed with a short hint, section 2 normal
One or more Nothing Section 1 normal, section 2 collapsed with a "Find spaces to join" link
One or more One or more Both sections normal

The empty states are intentionally friendly and short. The goal is to point new members at the next action, not to make them feel like they are missing out.

Privacy

The My Spaces page is personal to the signed-in user.

  • It requires sign-in. Signed-out visitors are bounced to login.
  • It is excluded from search engine indexing via the noindex, nofollow meta tag.
  • It is not visible to anyone else. There is no public URL that shows another user's space list.
  • Membership in a Hidden space is shown on this page but is still not visible on the user's public profile.

If you want to see which spaces another user is in, you have to look at their public profile, which only shows public memberships.

Performance

The page paginates server-side at 25 spaces per section. Most members never trigger pagination because they belong to far fewer than 25 spaces. Communities with very active staff may see paginated results in the "Spaces you run" section.

Unread counts are read from the same per-user read-status table used everywhere else in Jetonomy. Loading My Spaces does not trigger a separate count query per space; the page issues one batched query and renders.

What's Next?

The My Spaces page is one entry into your community life. The full profile page covers everything else: activity feed, badges, trust level, and account settings.

Your Profile Page →

Pro Features

Premium extensions: reactions, messaging, polls, badges, analytics, and more.

Add expressive emoji reactions to every post and reply - so members can respond instantly without writing a full reply.

PRO - This feature requires Jetonomy Pro.

Reaction chips below a community post

What You Will Learn

  • How to enable Reactions for your community
  • How to customize which emojis appear per space
  • How members react to and change their reaction on a post
  • How to read reaction counts from the REST API

Why Reactions Matter

A quick reaction lowers the bar for engagement. Members who would never write a reply will tap a rocket emoji or a heart. That micro-engagement adds up - you get richer signal on your best content and members feel heard without the pressure of composing a response.

How It Works

Every post and every reply in your community shows a reaction strip. Members click any emoji to react. Clicking a different emoji replaces the previous one - each member can hold exactly one reaction per piece of content at a time. Clicking the same emoji again removes the reaction entirely.

The reaction counts are displayed as chips directly below the post body. Each chip shows the emoji and the total count. Hovering a chip reveals the names of recent reactors.

Enabling Reactions

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Reactions and click Enable.
  3. Reactions appear on all posts and replies immediately - no page-level configuration needed.

Tip: Enabling Reactions does not affect any existing posts. Historical content simply starts with zero reactions.

Default Emoji Set

Jetonomy ships with six Fluent 3D emojis (Microsoft, MIT licensed) out of the box:

Emoji Label Use case
Like Thumbs up General agreement
Love Heart Enthusiasm, appreciation
Thinking Thinking face Interesting, thought-provoking
Celebrate Party popper Wins, announcements
Rocket Rocket Fast, shipped, love it
Sad Sad face Empathy, condolences

All six are enabled globally by default. You can adjust which ones appear per space.

Per-Space Customization

Different spaces have different tones. A Support space might not need a Celebrate emoji, and a General Chat space might not need Sad.

  1. Go to Jetonomy → Spaces and open the space you want to customize.
  2. Open the Reactions tab in the space settings panel.
  3. Toggle individual emojis on or off for this space.
  4. Save. The change takes effect immediately for all posts in that space.

REST API

The Reactions extension adds four endpoints under jetonomy/v1:

Method Endpoint Description
GET /reactions/{type}/{id} Get all reactions for a post or reply
POST /reactions/{type}/{id} Add or replace your reaction
DELETE /reactions/{type}/{id} Remove your reaction
GET /reactions/summary/{type}/{id} Aggregated counts by emoji

{type} is either post or reply. {id} is the numeric ID of the item.

Example - add a reaction:

POST /wp-json/jetonomy/v1/reactions/post/42
{
  "emoji": "rocket"
}

Example - get reaction summary:

GET /wp-json/jetonomy/v1/reactions/summary/post/42

{
  "like": 14,
  "love": 6,
  "rocket": 23,
  "celebrate": 2
}

All reaction endpoints require the user to be logged in. Reading summaries is open to any authenticated user; adding and removing reactions requires jetonomy_create_posts capability.

What's Next?

Allow members to message each other privately, without leaving your community.

Private Messaging →

Let members send direct messages to each other - one-on-one or in small groups - without leaving your community.

PRO - This feature requires Jetonomy Pro.

Messages inbox showing conversation list

What You Will Learn

  • How to enable Private Messaging
  • How members start conversations and send messages
  • How unread counts and notifications work
  • How to block users from messaging you
  • How to use the REST API for conversations and messages

Why Private Messaging Matters

When members can message each other directly, your community becomes a platform - not just a forum. It reduces off-site communication, keeps relationships within your ecosystem, and gives you a richer, stickier product.

How It Works

Private Messaging adds a dedicated inbox at /community/messages/. Members can start a new conversation with any other member, or create a group conversation with up to 20 participants. Each conversation is a persistent thread - messages appear in chronological order, and new messages are loaded automatically via polling.

Single conversation thread view

Enabling Private Messaging

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Private Messaging and click Enable.
  3. A Messages link appears automatically in the community navigation bar.

No additional configuration is required to go live.

Starting a Conversation

Members start a new conversation in two ways:

  • Click New Message from the Messages inbox at /community/messages/.
  • Click the Message button on any member's profile page.

Both methods open a composer. Members type the recipient's name (autocomplete searches by display name and username), write their first message, and hit Send. The conversation thread opens immediately.

For group conversations, members add multiple recipients before sending. The group conversation shows all participants' avatars at the top of the thread.

Unread Counts and Notifications

A red badge on the Messages nav icon shows the total number of unread conversations. The count updates every 30 seconds via polling. It drops to zero when a member opens and reads the conversation.

Jetonomy also sends a notification to the recipient's bell icon when a new message arrives. Members who have email notifications enabled for private messages receive an email notification as well.

Tip: Members control their messaging email notifications in Profile → Notification Settings. Admins cannot override individual user preferences.

Blocking Users

Any member can block another from sending them messages:

  1. Open a conversation with the person.
  2. Click the ··· menu in the thread header.
  3. Select Block [username].

Blocked users cannot send new messages to the member who blocked them. Existing conversation history is preserved but no new messages are delivered. The blocked user sees a generic "Unable to send message" error - they are not told they are blocked.

Admins can view and clear blocks in Jetonomy → Users → [username] → Messaging.

REST API

Private Messaging adds endpoints under jetonomy/v1:

Method Endpoint Description
GET /conversations List your conversations (paginated)
POST /conversations Start a new conversation
GET /conversations/{id} Get conversation details and participant list
GET /conversations/{id}/messages List messages (cursor-based pagination)
POST /conversations/{id}/messages Send a message
POST /conversations/{id}/read Mark conversation as read
DELETE /conversations/{id} Leave a conversation
POST /blocks Block a user from messaging you
DELETE /blocks/{user_id} Unblock a user

All endpoints require authentication. The jetonomy_send_messages capability controls who can start conversations - by default all Trust Level 1+ members.

Example - start a conversation:

POST /wp-json/jetonomy/v1/conversations
{
  "recipients": [45, 67],
  "message": "Hey, wanted to follow up on your question about onboarding."
}

Example - get messages with cursor pagination:

GET /wp-json/jetonomy/v1/conversations/12/messages?after=msg_abc123&per_page=20

What's Next?

Add polls to any topic to gather community input and drive decisions.

Polls →

Attach a poll to any topic and let your community vote - perfect for decisions, feedback, and feature prioritization.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to enable Polls
  • How to attach a poll when creating or editing a topic
  • How members vote, change their vote, and remove it
  • How poll results are displayed
  • How poll creators close a poll

Why Polls Matter

Asking a question in text is passive. Attaching a poll turns the same question into an action - members click an option in seconds instead of writing a response. You get quantified signal, higher engagement on that post, and a result the whole community can see at a glance.

Enabling Polls

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Polls and click Enable.
  3. A + Add Poll button appears at the bottom of the post composer.

Creating a Poll

When writing a new topic, click + Add Poll beneath the content editor.

The poll builder lets you:

  • Write up to 10 options (minimum 2 required).
  • Choose Single choice (members pick one) or Multiple choice (members pick several).
  • Optionally set a Close date - the poll stops accepting votes automatically at that date and time.

The poll is attached to the topic and saved together when you click Post. You cannot attach a poll to a reply - only to top-level topics.

Tip: You can add or remove a poll from an existing topic by editing the post. Removing a poll permanently deletes all votes cast on it.

How Members Vote

The poll appears below the post body, before any replies. Each option is displayed as a labeled button.

  • Single choice - clicking an option records your vote immediately. You see the results as percentage bars as soon as you vote.
  • Multiple choice - checkboxes appear. Select all the options you want and click Vote.

After voting, members can change their vote by clicking a different option. Clicking your current selection a second time removes your vote entirely.

Members who have not voted see the options. Members who have voted see the live results. This design keeps undecided members from being anchored by early results.

Reading Results

Results display as horizontal percentage bars with the option label, the percentage, and the raw vote count. Bars fill in proportion to the leading option.

The total vote count appears below the bar chart. If the poll has a close date, a countdown shows how much time remains.

Closing a Poll

The topic author and any space moderator can close a poll at any time:

  1. Open the topic.
  2. Click the ··· menu on the poll card.
  3. Select Close Poll.

Once closed, the poll shows a Closed badge and no further votes are accepted. Existing results remain visible. A closed poll can be re-opened by the same users using the same menu.

Note: If you set a close date and later want to extend it, edit the post and update the date in the poll settings.

What's Next?

Collect structured information about your members with custom profile fields.

Custom Profile Fields →

Add structured fields to member profiles - collect the information that matters to your specific community.

PRO - This feature requires Jetonomy Pro.

As of 1.4.1, custom field values are exposed on the REST API for both posts and users. Earlier versions only saved the values to the database; third-party API consumers couldn't read them because the response filters were registered against an event nothing emitted. Free 1.4.1 fires the matching filters on /jetonomy/v1/posts and /jetonomy/v1/users, so any tool reading from the API now sees every custom field you have configured.

Custom fields displayed on a member profile page

What You Will Learn

  • How to create and manage custom profile fields
  • Which field types are available
  • How to set visibility and required status
  • How members fill in their fields
  • How to read and update field values via the REST API

Why Custom Fields Matter

A generic WordPress profile has a bio and a website URL. That is not enough for most communities. A developer community needs a GitHub handle. A healthcare community needs a specialty. A SaaS community needs a company name. Custom Fields lets you define exactly the information that makes members useful and findable in your community.

Enabling Custom Fields

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Custom Fields and click Enable.
  3. A Custom Fields item appears under the Jetonomy admin menu.

Creating a Field

  1. Go to Jetonomy → Custom Fields.
  2. Click Add Field.
  3. Fill in the field settings:
Setting Description
Label Displayed on the profile and in the edit form
Field Key Unique slug used in the REST API (auto-generated, editable)
Type See field types below
Required If on, members cannot save their profile without filling this in
Visibility Who can see the field value
Description Optional helper text shown below the input
  1. Click Save Field.

Field Types

Type Best for
Text Short single-line answers (job title, company, username)
Textarea Longer free-form text (bio supplement, expertise description)
Select Predefined options (country, role, industry)
Checkbox Yes/no toggle (newsletter opt-in, open to work)
URL Website, GitHub, LinkedIn, portfolio links

For the Select type, you define the options in the field editor - one per line. The member sees a dropdown.

Visibility Options

Each field has one of three visibility settings:

Visibility Who sees the value
Public Anyone, including logged-out visitors
Members only Logged-in community members
Private Only the member themselves and admins

Private fields are still editable by the member but do not appear on their public profile. They are accessible to admins via the admin Users page.

How Members Fill In Fields

Members edit their custom fields at /community/u/{username}/edit/ under the Profile Details section. Required fields show a red asterisk. Saving the profile validates all required fields before updating.

Tip: Guide members to complete their profiles right after joining by linking to the edit profile page in your welcome notification or email digest.

REST API

Custom Fields adds field-aware parameters to the existing profile and post endpoints:

Method Endpoint Description
GET /users/{id} Profile response includes custom_fields object
GET /users List response includes custom_fields on each member
PATCH /users/{id} Pass custom_fields: { field_key: value } to update
GET /posts/{id} Post response includes custom_fields if any post-level fields are configured
GET /posts List response includes custom_fields on each post
GET /custom-fields List all defined fields and their settings

Example - read a profile with custom fields:

GET /wp-json/jetonomy/v1/users/45

{
  "id": 45,
  "name": "Priya Sharma",
  "custom_fields": {
    "company": "Acme Corp",
    "github_username": "priyasharma",
    "open_to_work": true
  }
}

Example - update custom fields:

PATCH /wp-json/jetonomy/v1/users/45
{
  "custom_fields": {
    "company": "New Horizons Ltd"
  }
}

Members can only update their own fields. Admins can update any member's fields.

What's Next?

Recognize and reward your most engaged members with custom badges.

Custom Badges →

Design your own badges, set the conditions that earn them, and watch members compete to collect them.

PRO - This feature requires Jetonomy Pro.

Badges displayed on a member profile page

What You Will Learn

  • How to create a badge with a name, icon, and tier
  • How to set auto-award conditions
  • How to award badges manually as an admin
  • How badges display on member profiles

Why Custom Badges Matter

Trust levels and reputation points are invisible to casual members. Badges are visible, collectible, and shareable - they give members a concrete goal to aim for. A "100 Posts" badge tells the community this member is active. A "Top Answerer" badge signals expertise. Badges convert passive lurkers into active contributors.

Enabling Custom Badges

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Custom Badges and click Enable.
  3. A Badges item appears under the Jetonomy admin menu.

Creating a Badge

  1. Go to Jetonomy → Badges.
  2. Click Create Badge.
  3. Fill in the badge details:
Field Description
Name Displayed on the badge and in the award notification
Description One sentence explaining how to earn it
Icon Upload a 64×64 PNG or SVG icon
Tier Bronze, Silver, or Gold - controls the border color on profile
  1. Set the award conditions (see below).
  2. Click Save Badge.

Award Conditions

Auto-Award

Auto-awarded badges evaluate all members on a regular schedule and grant the badge automatically when the conditions are met. Choose from built-in criteria:

Criteria Example threshold
Total posts 10, 50, 100, 500 posts
Accepted answers 5, 25, 50 accepted answers
Total replies 25, 100, 250 replies
Upvotes received 10, 50, 200 upvotes on any content
Days as member 30, 180, 365 days since joining

Set the threshold for each criteria you want to use. You can combine multiple criteria - the member must satisfy all of them to earn the badge.

Note: Auto-evaluation runs once every 12 hours via WP-Cron, aligned with the trust level evaluation job. There is no manual "evaluate now" button, but you can trigger evaluation by going to Jetonomy → Badges → Run Evaluation.

Manual Award

Some badges should not be automated - "Staff Pick", "Most Helpful in July", or "Community Founder" are judgment calls. For these, leave all auto-award criteria blank and award manually:

  1. Go to Jetonomy → Users and open the member's profile.
  2. Click Award Badge.
  3. Select the badge from the list and add an optional private note.
  4. Click Award.

The member receives a notification immediately and the badge appears on their profile.

Badge Tiers

Badges have three visual tiers that appear as border colors on the badge icon:

Tier Color Suggested use
Bronze Warm bronze Entry-level milestones (first post, 7-day streak)
Silver Cool silver Mid-tier milestones (100 posts, 10 accepted answers)
Gold Bright gold Elite milestones (500 posts, Top Contributor of the year)

Tiers are visual only - they do not affect permissions or trust levels.

Badge Display

Badges appear on member profile pages in a dedicated Badges section. Members who have earned no badges see an empty state that lists a few featured badges to work toward - this passively encourages engagement.

The three most recently earned badges also appear in the member's hover card, which pops up when anyone hovers their username throughout the community.

What's Next?

Get a data-driven view of your community's health with the Analytics Dashboard.

Analytics Dashboard →

Understand what your community is doing, where it is growing, and who is driving it - all from a single admin dashboard.

PRO - This feature requires Jetonomy Pro.

Analytics dashboard showing charts and top contributor table

What You Will Learn

  • How to access the Analytics dashboard
  • Which metrics are tracked and what they mean
  • How to use the date range filter
  • How to export data as CSV

Why Analytics Matter

You cannot grow what you cannot measure. The Analytics dashboard turns raw community activity into actionable metrics - so you know which spaces need attention, who your power users are, and whether engagement is trending up or down before it becomes a problem.

Enabling Analytics

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Analytics and click Enable.
  3. An Analytics item appears under the Jetonomy admin menu.

Analytics begin recording from the moment you enable the extension. Historical data before activation is not backfilled.

Navigating the Dashboard

Go to Jetonomy → Analytics. The dashboard opens on the last 30 days by default.

Date Range Filter

Use the date range picker at the top right to select any custom range. Preset shortcuts:

  • Last 7 days
  • Last 30 days
  • Last 90 days
  • This month
  • Last month

All charts and tables update instantly when you change the range.

Overview Metrics

The top row shows four headline numbers for your selected range:

Metric What it means
Total Posts New topics created in the period
Total Replies New replies created in the period
Active Members Unique members who posted, replied, or voted
Growth Rate Percentage change vs the previous equal-length period

Below the headline row, a line chart shows daily post and reply volume over the selected range. Spikes are easy to spot - hover any point to see the exact date and count.

Top Spaces

A ranked table shows your most active spaces sorted by total posts + replies in the period. Columns include post count, reply count, unique contributors, and engagement rate (replies per post).

Use this view to find spaces that are thriving - and spaces that have gone quiet and may need a prompt or a featured topic.

Top Contributors

A ranked table of your most active members sorted by total contributions (posts + replies + accepted answers). Each row shows the member's avatar, display name, trust level, and contribution breakdown.

Tip: Use this list to identify members to invite into a moderator role, feature in a community spotlight, or send a personal thank-you.

Engagement Metrics

The engagement section shows:

Metric What it means
Avg. replies per post How much discussion each new topic generates
Vote activity Total upvotes and downvotes cast
Accepted answers Q&A spaces only - rate of questions getting resolved
New member joins Community growth over the period

Moderation Stats

The moderation section shows flagged content volume, auto-moderation rule triggers (if Advanced Moderation is enabled), and moderator response time. A high flag volume combined with slow response time signals you need more moderators.

CSV Export

Click Export CSV in the top right of any table to download a full data export for the current date range. Exports include all rows - not just the visible page.

Exported files are compatible with Excel, Google Sheets, and any BI tool. Column headers match the on-screen labels exactly.

Dual-Path Aggregator (1.4.1, observation only)

In 1.4.1 a second analytics path runs quietly alongside the existing direct-query reader. It's an event-driven aggregator that writes to a new internal table (jt_pro_analytics_aggregate) on every relevant community event, instead of querying core tables every time the dashboard loads.

The aggregator is in a 7-day observation window. Your dashboards still read from the original direct-query path in 1.4.1 - there is no public-facing behaviour change yet. The point of the observation window is to verify that the two paths agree before flipping the default.

Verify Dual-Path admin toggle

Go to Jetonomy → Analytics. The Pro Analytics page now has a Verify dual-path toggle. Turn it on and every metric on the page is shown twice - once from the direct-query reader, once from the aggregator - with a drift percentage between them. Use this if you want to spot-check the aggregator's accuracy before we promote it to default.

GET /jetonomy/v1/analytics/diff-report

For programmatic comparison, the same data is available at:

GET /wp-json/jetonomy/v1/analytics/diff-report?range=30d

{
  "range": "30d",
  "metrics": [
    { "key": "total_posts", "direct": 1284, "aggregated": 1284, "drift_pct": 0 },
    { "key": "active_members", "direct": 312, "aggregated": 311, "drift_pct": -0.32 }
  ]
}

Helpful if you want to ship a custom monitoring panel during the observation window.

What's Next?

Reduce your moderation workload by automating common moderation decisions.

Advanced Moderation Rules →

Define rules that catch bad content automatically - before it ever appears in your community.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to enable Advanced Moderation Rules
  • How to create keyword, regex, link-limit, and spam-score rules
  • What actions each rule can take on matched content
  • How to scope rules to a specific space or apply them globally
  • How to read rule trigger statistics

Why Auto-Moderation Matters

Manual moderation does not scale. A single moderator reviewing every post works fine at 10 posts per day - it fails at 1,000. Auto-moderation rules handle the obvious cases automatically so your human moderators can focus on edge cases that require judgment.

Advanced Moderation complements the free trust level system. New members with Trust Level 0 are already rate-limited. Auto-moderation rules add a content layer on top of that.

Enabling Advanced Moderation

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Advanced Moderation and click Enable.
  3. A Moderation Rules tab appears under Jetonomy → Moderation.

Creating a Rule

  1. Go to Jetonomy → Moderation → Rules.
  2. Click Add Rule.
  3. Configure the rule:
Setting Description
Name Internal label - members never see this
Pattern type Keyword, Regex, Link limit, or Spam score
Pattern The word, phrase, regex, or threshold to match
Action What happens when the rule triggers
Scope Global (all spaces) or a specific space
Active Enable or disable the rule without deleting it
  1. Click Save Rule.

Pattern Types

Keyword

Matches any post or reply body that contains the exact word or phrase (case-insensitive). Use comma-separated values to match any of several words with a single rule.

Example: buy now, click here, limited offer

Regex

Full regular expression matched against the post body. Use this for patterns a keyword list cannot capture - phone number patterns, URL shortener patterns, or obfuscated spam.

Example: \b(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b

Note: Regex patterns are evaluated server-side using PHP preg_match(). Test your regex at regex101.com before adding it to a live rule.

Link Limit

Triggers when a post or reply contains more than a set number of links. New spammers often post content with 5-10 outbound links. A limit of 3 catches most of these while allowing legitimate "here are some resources" posts.

Spam Score

Jetonomy calculates a spam probability score (0-100) for each post based on content patterns, account age, and posting frequency. Set a threshold - any post above that score triggers the rule.

A threshold of 80 is a good starting point. Lower it only if you are seeing spam slip through.

Rule Actions

Action What happens
Flag Content publishes normally and is added to the mod queue with a flag
Hold Content is held as Pending and does not appear until a moderator approves it
Block The post is rejected and the member sees an error message
Spam Content is marked as spam and hidden immediately

Choose the least restrictive action that solves the problem. Use Flag for borderline content, Hold for likely-bad content, and Block or Spam for clearly harmful content.

Rule Scope

Global rules apply to every post and reply across all spaces. Use these for site-wide policies - prohibited words, adult content, competitor spam.

Space-scoped rules apply only within a specific space. Use these for space-specific norms - a Support space might block all links to prevent fishing attacks, while General Chat allows them freely.

Rule Statistics

The rules list shows a Triggered count for each rule - how many times it has fired since the rule was created. Click a rule to see a breakdown by action, by space, and a timeline chart.

Use this data to tune your rules. A rule that triggers 500 times in a week and sends everything to Spam probably needs a higher threshold - it is catching legitimate content.

What's Next?

Re-engage members who have not visited recently with automated email digests.

Email Digest →

Send a curated summary of community activity to members' inboxes - daily or weekly - so they never feel out of the loop.

PRO - This feature requires Jetonomy Pro.

Email digest preview showing top posts from the week

What You Will Learn

  • How to enable and configure the Email Digest
  • What content appears in each digest
  • How members control their digest frequency
  • How to send a test digest as an admin

Why Email Digest Matters

Most community members are not daily visitors. They join, participate a few times, and drift away - not because they lost interest, but because they forgot. A well-timed email digest brings them back. It shows them what they missed and gives them a reason to click.

Enabling Email Digest

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Email Digest and click Enable.
  3. Go to Jetonomy → Settings → Email Digest to configure sending times and content rules.

Configuring the Digest

Send Schedule

Set when the digests go out:

Digest type Default send time
Daily 8:00 AM (site timezone)
Weekly Monday, 8:00 AM (site timezone)

You can change both send times in the Email Digest settings. Times use the timezone set in Settings → General → Timezone in WordPress.

Digest Content

The digest compiles:

  • Top posts - the most-upvoted new topics since the last digest
  • Active discussions - posts with the most replies in the period
  • Spaces you follow - activity in spaces the member has joined or bookmarked
  • Replies to your topics - new replies on topics the member created or commented on
  • 🏆 Badges earned (new in 1.4.1) - every badge the member earned during the digest window
  • 🗳️ Polls voted on (new in 1.4.1) - every poll the member participated in during the window

You can toggle each content section on or off in the digest settings. At least one section must remain on.

How the new sections work: badges and polls are tracked in a per-user buffer that's capped at 100 events with a 30-day TTL. The buffer is cleared only after a successful send, so opted-out members never accumulate state, and a missed send doesn't lose the activity.

Tip: Keeping "Replies to your topics" on is the single most effective setting. Members always care more about activity on their own posts than about the broader community.

Member Notification Preferences

Each member controls their own digest frequency from Profile → Notification Settings → Email:

Option What it means
Instant Individual emails per event (free behavior)
Daily digest One email per day summarizing activity
Weekly digest One email per week summarizing activity
None No community emails

Members who select Daily or Weekly stop receiving per-event notification emails. The digest replaces them - they are not sent in addition to them.

New members default to Daily digest. You can change this default in the Email Digest settings.

Admin Test Send

Before going live, send yourself a test digest to check formatting and content:

  1. Go to Jetonomy → Settings → Email Digest.
  2. Scroll to the Test Send section.
  3. Enter an email address (pre-filled with your admin email).
  4. Click Send Test Digest.

The test digest uses real community data from the last 7 days. If your community is new and has little activity, the digest may look sparse - that is expected.

Digest Statistics

The Email Digest settings page shows send statistics for the last 90 days:

Stat Description
Digests sent Total number of emails delivered
Preference breakdown How many members are on daily vs weekly vs none

Open rates and click rates are available if you use a supported ESP adapter (SendGrid, Mailgun, SES, or Postmark) - basic wp_mail delivery does not provide tracking data.

What's Next?

Connect your community to external tools like Slack, CRMs, and Zapier using outbound webhooks.

Outbound Webhooks →

Automatically send community events to any external URL - connect Jetonomy to Slack, Zapier, your CRM, or any custom pipeline.

PRO - This feature requires Jetonomy Pro.

As of 1.4.1, every documented event actually fires. Earlier versions registered listeners against hook names the free plugin never emitted, so deployments would test the webhook UI, see "delivered," and never receive a single real-world event afterwards. Free 1.4.1 ships the lifecycle action contract Pro now hooks into. If you previously configured webhooks and saw zero deliveries despite real activity, update both plugins to 1.4.1 and the existing webhooks will start working without any reconfiguration.

Webhook management page listing configured endpoints

What You Will Learn

  • How to create and manage webhook endpoints
  • Which community events you can subscribe to
  • How to test a webhook before going live
  • How to read the delivery log to debug failures

Why Webhooks Matter

Jetonomy lives inside WordPress - but your business does not. Your team lives in Slack. Your sales data lives in a CRM. Your analytics live in a data warehouse. Webhooks close that gap. Every time something happens in your community, Jetonomy can push that data wherever you need it - in real time, with zero polling.

Enabling Webhooks

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find Outbound Webhooks and click Enable.
  3. A Webhooks item appears under the Jetonomy admin menu.

Creating a Webhook

  1. Go to Jetonomy → Webhooks.
  2. Click Add Webhook.
  3. Fill in the endpoint settings:
Setting Description
URL The HTTPS endpoint that will receive the POST request
Events Which community events trigger this webhook
Secret Optional signing secret for verifying payload authenticity
Active Toggle the webhook on or off without deleting it
  1. Click Save Webhook.

Available Events

Subscribe to any combination of these events:

Event Fires when...
post.created A new topic is published
post.updated A topic is edited
post.deleted A topic is deleted or trashed
reply.created A new reply is posted
reply.updated A reply is edited
reply.deleted A reply is deleted or trashed
vote.cast A member votes on a post or reply
answer.accepted A Q&A answer is accepted
member.joined A new user joins the community
member.left A member leaves a space
member.banned A member is banned
user.registered A new user account is created
trust_level.changed A member is promoted or demoted
flag.created Content is flagged by a member
flag.resolved A flag is resolved by a moderator
moderation.action A moderator approves, spams, or trashes content

You can create multiple webhooks pointing to different URLs with different event subsets - for example, one webhook for Slack (post events only) and one for your CRM (member events only).

Payload Format

Every webhook delivers a JSON body with this structure:

{
  "event": "post.created",
  "timestamp": "2026-03-26T08:14:32Z",
  "site_url": "https://yoursite.com",
  "data": {
    "post_id": 1024,
    "title": "How do I reset my password?",
    "author_id": 45,
    "author_name": "Priya Sharma",
    "space_id": 3,
    "space_slug": "support",
    "url": "https://yoursite.com/community/s/support/t/how-do-i-reset-my-password/"
  }
}

The data object varies by event type. All events include event, timestamp, and site_url.

Verifying Payloads

If you set a secret, Jetonomy includes an X-Jetonomy-Signature header with each request. The value is an HMAC-SHA256 signature of the raw request body, signed with your secret.

Verify on your server:

$signature = hash_hmac( 'sha256', $raw_body, $your_secret );
$expected   = 'sha256=' . $signature;
$received   = $_SERVER['HTTP_X_JETONOMY_SIGNATURE'];

if ( ! hash_equals( $expected, $received ) ) {
    http_response_code( 401 );
    exit;
}

Testing a Webhook

Before you point a webhook at a production system, send a test:

  1. Open the webhook in Jetonomy → Webhooks.
  2. Click Send Test.
  3. Jetonomy sends a sample ping event to your URL and shows the HTTP response code and body inline.

Use a tool like webhook.site to inspect the full request during development.

Delivery Log

Every delivery attempt is logged. Go to Jetonomy → Webhooks → [webhook name] → Delivery Log to see:

  • Timestamp of each attempt
  • HTTP response code
  • Response body (first 500 characters)
  • Whether the delivery succeeded or failed

Failed deliveries are retried up to three times with exponential backoff (5 min, 30 min, 2 hrs). After the third failure, the delivery is marked as permanently failed and no further retries are attempted.

Tip: If you see consistent failures, check that your endpoint returns a 2xx response within 10 seconds. Jetonomy times out at 10 seconds and treats the delivery as failed.

Use Case Examples

  • Slack notifications - Post a message to a Slack channel whenever a new topic is created in your community.
  • CRM sync - Push member.joined events to HubSpot or Salesforce to trigger a welcome sequence.
  • Zapier - Connect any event to thousands of apps via a Zapier webhook trigger - no custom code needed.
  • Analytics pipeline - Stream all events to a data warehouse like BigQuery or Amplitude for long-term retention and custom analysis.

What's Next?

Send browser push notifications to members even when they are not on your site.

Web Push Notifications →

Reach members with browser push notifications - even when they have closed your site.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to generate VAPID keys and enable Web Push
  • How members subscribe to push notifications
  • Which events trigger a push notification
  • Which browsers are supported

Why Web Push Matters

Email notifications have open rates around 20-30%. Browser push notifications have open rates above 60% because they appear immediately on the member's screen - no inbox, no subject line, no waiting. For time-sensitive events like a reply to your question or a mention from a teammate, push gets the message there instantly.

Enabling Web Push

Web Push requires a VAPID key pair to authenticate your server with browsers.

  1. Go to Jetonomy → Settings → Web Push.
  2. Click Generate VAPID Keys. Jetonomy creates a public/private key pair and stores them in your WordPress options table.
  3. Toggle Enable Web Push to on.
  4. Click Save.

Important: VAPID keys are generated once. If you regenerate them, all existing push subscriptions are invalidated and members must subscribe again. Only regenerate if you believe your private key has been compromised.

Web Push settings page with VAPID key fields and enable toggle

Service Worker Registration

Jetonomy automatically registers a service worker (/community/sw.js) on every community page. You do not need to create or configure the service worker - this happens at extension activation.

The service worker handles:

  • Receiving push messages from the Jetonomy server
  • Displaying the browser notification
  • Opening the correct URL when the notification is clicked

Member Subscription

The first time a logged-in member visits any community page after you enable Web Push, a Enable push notifications prompt appears at the top of the page. Clicking Enable triggers the browser's native permission dialog.

Members who grant permission are subscribed automatically. Their subscription is stored in the Jetonomy database and associated with their account.

Members can unsubscribe at any time from Profile → Notification Settings → Push Notifications → Off.

Note: The browser permission prompt can only be triggered by a user action (a click). Jetonomy waits for the member to interact with the prompt banner before requesting permission - it never requests permission automatically on page load.

Notification Events

Push notifications are sent for the same events that trigger bell notifications, based on each member's preferences:

Event Who receives a push
New reply on your topic Topic author
New reply in a topic you follow Followers
You are mentioned Mentioned member
Your answer is accepted Answer author
New message received Message recipient (Private Messaging Pro)
Badge awarded Badge recipient

Members control which of these events trigger a push in Profile → Notification Settings.

Browser Support

Web Push works on all major modern browsers without any app installation:

Browser Desktop Mobile
Chrome Yes Yes (Android)
Edge Yes Yes (Android)
Firefox Yes Limited
Safari Yes (macOS 13+) Yes (iOS 16.4+)

Tip: Safari on iOS requires members to add your site to their Home Screen before push notifications work. This is an Apple platform limitation - not a Jetonomy limitation.

What's Next?

Let members reply to community topics directly from their email client.

Reply by Email →

Let members reply to community discussions directly from their email client - no login required for that one reply.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How Reply by Email works end to end
  • How to configure the inbound email endpoint
  • How emails are parsed and turned into replies
  • How to test the feature and handle parsing errors

Why Reply by Email Matters

Every step between "I got a notification" and "I posted a reply" loses members. Reply by Email removes all those steps. The member reads the notification in their inbox, types a reply directly, hits Send, and the reply appears in the community - without opening a browser or logging in. Removing that friction increases reply volume noticeably.

How It Works

  1. Jetonomy sends a notification email for a new reply or mention.
  2. The email contains a Reply to this post call to action with a unique reply-to address.
  3. The member replies to that email.
  4. The inbound email is delivered to your configured endpoint.
  5. Jetonomy parses the email, strips quoted content, and creates a community reply attributed to that member.

Each reply-to address is unique to the member and the topic - it encodes an authentication token so no login is required.

Configuration

Reply by Email requires an inbound email endpoint - a URL that receives incoming emails from your email provider. Most email providers (SendGrid Inbound Parse, Mailgun Inbound Routes, Postmark Inbound, or Amazon SES with SNS) can forward inbound email as an HTTP POST to a URL.

Step 1: Get Your Inbound Endpoint URL

  1. Go to Jetonomy → Settings → Reply by Email.
  2. Copy the Inbound Endpoint URL. It looks like:
https://yoursite.com/wp-json/jetonomy/v1/email/inbound

Step 2: Configure Your Email Provider

Point your email provider's inbound parsing feature at the Jetonomy endpoint URL. The exact steps vary by provider - follow your provider's documentation for "inbound email parsing" or "inbound routing."

Set up a dedicated inbound domain or subdomain for replies. Example: reply.yoursite.com. Your provider resolves inbound mail sent to *@reply.yoursite.com and forwards the parsed payload to your Jetonomy endpoint.

Step 3: Enter Your Reply Domain

Back in Jetonomy → Settings → Reply by Email, enter the reply domain (e.g. reply.yoursite.com) and click Save.

Jetonomy now generates per-user, per-topic reply addresses using that domain.

Email Parsing

Jetonomy parses the incoming email using these rules:

  1. Strip quoted content - Lines that begin with > (standard email quoting) are removed. The reply contains only the new text the member typed.
  2. Plain text preferred - If the email has a plain text part, Jetonomy uses that. If not, it strips HTML and uses the text content.
  3. Basic formatting preserved - Line breaks are preserved. Links in the plain text body are converted to Markdown links.
  4. Attachments ignored - Image and file attachments in reply emails are not processed in v1.0.

The parsed reply text goes through the same wp_kses_post sanitization as any other reply before it is saved.

Parsing Rules Configuration

You can adjust how Jetonomy handles edge cases:

Setting Default Description
Min reply length 5 characters Rejects replies shorter than this (catches accidental sends)
Max reply length 10,000 characters Truncates anything longer
Strip signatures On Removes common signature separators (-- , Sent from my iPhone, etc.)

Security

Each reply-to address contains a signed token that ties the email address to a specific WordPress user and topic. Jetonomy verifies the token before creating any reply. An attacker who intercepts or guesses a reply address cannot post as another member - the token is cryptographically bound to the user ID.

Tokens expire after 30 days. Notification emails older than 30 days cannot be replied to by email.

Testing Reply by Email

  1. Go to Jetonomy → Settings → Reply by Email.
  2. Click Send Test Email to send a sample notification to your admin email address.
  3. Reply to that email with any text.
  4. Return to the admin and click Check Last Inbound to see the parsed result.

What's Next?

Remove all Jetonomy branding and present the community as entirely your own.

White Label & Branding →

Remove all Jetonomy branding and present your community as entirely your own product.

PRO - This feature requires Jetonomy Pro.

As of 1.4.1, every White Label setting actually applies on every surface. Header logo, footer text, email accent colour, email logos, the sidebar sign-in card, and admin footer text all rebrand on every send / render. Earlier versions defined the filters but nothing was hooking into them, so changes to the Branding settings had no visible effect on customer sites. Free 1.4.1 ships the matching Jetonomy\header_logo() and Jetonomy\footer_text() helpers Pro hooks into.

What You Will Learn

  • How to enable White Label
  • How to remove Jetonomy branding from the frontend and admin
  • How to set a custom admin menu label and icon
  • How to control branding for headless or REST API clients

Why White Label Matters

You built your community. Your members know your brand - not the plugin powering it. White Label means your community looks like yours from every angle: the frontend pages, the admin sidebar, and the REST API responses. This is especially important for agencies delivering client projects and for SaaS products embedding community features under their own brand.

Enabling White Label

  1. Go to Jetonomy → Extensions in your WordPress admin.
  2. Find White Label and click Enable.
  3. A Branding tab appears under Jetonomy → Settings.

Removing Frontend Branding

Go to Jetonomy → Settings → Branding.

Setting Default What it controls
"Powered by Jetonomy" footer Shown Remove the attribution link from the community footer
Jetonomy logo in community nav Shown Replace with your own logo or hide entirely
HTML data-plugin attribute jetonomy Change or remove the attribute on the .jt-app wrapper
Custom CSS injection Empty Add CSS that loads on every community page

Upload your own logo (SVG or PNG, max 400×100 px) to replace the Jetonomy logo in the community navigation bar. Leave the logo field blank to show no logo at all.

White Label branding settings panel

Tip: Use the Custom CSS injection field to apply brand-specific color overrides without editing any theme files. The CSS injects after Jetonomy's own stylesheet so your values always win.

Admin Menu Customization

By default, the Jetonomy admin menu item is labeled "Jetonomy" with the Jetonomy logo icon.

In Jetonomy → Settings → Branding → Admin Menu:

  • Menu label - Change to any string (e.g. "Community", "Forum", "My Community").
  • Menu icon - Enter any Dashicons class (e.g. dashicons-groups) or leave blank to use the default.

The label change applies to the top-level menu item and the browser window title on all Jetonomy admin pages.

REST API Branding

By default, Jetonomy's REST API responses include a powered_by key in the root namespace response:

GET /wp-json/jetonomy/v1/

{
  "name": "Jetonomy API",
  "powered_by": "Jetonomy"
}

With White Label enabled, you can override both the name and powered_by values in Settings → Branding → REST API Label. Set them to your product name, or leave them blank to omit those fields entirely from the response.

This is particularly useful for headless community builds where the REST API is consumed by a custom frontend - clients see your brand name, not Jetonomy's.

Email Branding

White Label also affects transactional emails and digests. In Settings → Branding → Email:

  • From name - Defaults to your site name. Change to any value.
  • Email footer - Replaces the default Jetonomy email footer with your own text or HTML.
  • Logo in emails - Upload a logo displayed at the top of notification emails.

What's Next?

You have covered all 12 Pro features. Return to the beginning of the Pro section to explore Emoji Reactions and other extensions.

Emoji Reactions →

Bring large language models into your community - for smarter spam detection, auto-moderation, reply suggestions, and thread summaries - without sending data to a third party unless you want to.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How the AI extension is organized (providers + features)
  • Which AI providers Jetonomy supports, including self-hosted Ollama
  • How to enable AI-powered spam detection, content moderation, reply suggestions, and thread summaries
  • Where to monitor token usage and cost
  • How to keep community content private by running models locally

Why AI Matters for Communities

Moderation is the single biggest burden on community owners. Every post and every reply is a potential spam attempt, abuse report, or duplicate question. At 50 topics a day, a human moderator can keep up. At 500, they cannot.

The Jetonomy Pro AI extension gives your community a language model that reads every new post and every new reply, flags the ones that need a human, and leaves the rest alone. It does not replace your moderators - it filters what they see so they can focus on judgment calls instead of the obvious cases.

Supported Providers

Jetonomy's AI layer is built on a pluggable adapter pattern. Four providers ship out of the box:

Provider Hosted by Best for
Ollama You - runs on your own server Privacy-sensitive communities, GDPR, full data control
OpenAI OpenAI Fastest time to value, best general quality
Anthropic Anthropic (Claude) High-quality moderation, long context windows
Custom You Self-hosted vLLM, LM Studio, or any OpenAI-compatible endpoint

You can register more than one provider and assign different features to different providers - for example, run moderation through Ollama locally while using OpenAI only for summaries of long threads.

Tip: If you already run Ollama on the same server as WordPress, the AI extension uses it over localhost - no network hop, no API key, no data leaves your machine.

Multi-Provider Fallback Chain (Pro 1.3.0+)

Every AI feature can be assigned a primary provider plus an optional fallback chain. If the primary provider errors mid-request (rate limit, timeout, 5xx), Jetonomy automatically retries the next provider in the chain before surfacing an error to the admin.

Typical setup: Ollama (primary) → OpenAI (fallback) → Anthropic (second fallback). You get privacy-first moderation with a safety net for the moments when the local model is restarting or overloaded.

The chain is configured per feature at Jetonomy → Settings → AI Integration → Advanced. Requests that succeed via the fallback chain are logged with a fallback_used flag so you can spot providers that are flapping.

Monthly Spend Caps (Pro 1.3.0+)

Every cloud provider (OpenAI, Anthropic, Custom) can be given a monthly spend cap in USD. Once usage crosses the cap in the current billing window, Jetonomy stops dispatching requests to that provider and surfaces a clear admin notice explaining what happened and when the cap resets.

Spend caps protect you from a runaway loop - a broken summary trigger, a spam wave that hits the reply-suggestion endpoint - draining your account. Self-hosted Ollama has no cap UI because it has no per-request cost.

Caps are evaluated against the usage log (wp_jt_pro_ai_usage), not the provider's invoice, so the cap reacts immediately. Expect a 2-3% variance versus your invoice at the end of the month because Jetonomy's estimate uses published token rates.

AI-Powered Features

Each feature can be toggled independently from Jetonomy → Settings → AI Integration.

Spam Detection

Replaces the free plugin's pattern-based spam detector with a model-driven classifier. Each new post or reply is scored for spam probability before it is published. Posts above the threshold go straight to the moderation queue; posts below it are published as normal.

Works alongside Akismet and trust-level rate limits - AI spam detection is the third layer, not a replacement.

Content Moderation

Flags content that breaks rules you describe in plain English. You write a few sentences - "no political attacks, no personal insults, no harassment" - and the model reads every new post against that policy.

Violations are sent to the moderation queue with a reason the model generated. Your human moderators make the final call, but they see a pre-filled explanation instead of a blank flag.

Moderation presets (Pro 1.3.0+) - Four tuned presets ship out of the box so you do not have to write a policy from scratch:

Preset Tuned for
Community Forum General discussion boards. Flags spam, harassment, obvious off-topic. Permissive on mild profanity and opinion-based disagreement.
Support Desk Product and service support. Flags abusive language toward staff, solicitation, and off-topic promotion. Permissive on frustration.
Kids Safe Under-13 communities. Aggressive filter - any profanity, sexual language, personal contact requests, or outside links flagged.
Academic University and research communities. Flags plagiarism indicators, academic dishonesty keywords, and personal attacks. Permissive on technical debate.

Pick a preset as your starting point and customize from there. The preset fills the policy text field - you edit it - and the model uses your edited copy.

Reply Suggestions

When a member is composing a reply, Jetonomy can ask the model for a draft based on the topic context. The member can accept it, edit it, or ignore it. Draft replies are never sent without human approval.

Great for knowledge-base communities where most answers follow a pattern.

Thread Summaries

On long threads (30+ replies), Jetonomy can generate a short summary pinned at the top of the topic. New visitors read the summary instead of scrolling through every back-and-forth. Summaries regenerate when new replies meaningfully change the conversation.

Cached in the wp_jt_pro_ai_cache table so each summary is generated once per content state.

Enabling AI Integration

  1. Go to Jetonomy → Extensions and enable AI.
  2. Open Jetonomy → Settings → AI Integration.
  3. Choose a provider. For Ollama, enter the base URL (usually http://localhost:11434) and the model name (for example, llama3.1:8b).
  4. Click Test Connection to verify credentials against the live provider before you go live. The button returns a success confirmation with the round-trip latency, or a clear error string from the provider.
  5. (Optional) Add a fallback provider and set a monthly spend cap at AI Integration → Advanced.
  6. Turn on the individual features you want - Spam Detection, Content Moderation, Reply Suggestions, or Thread Summaries. Pick a Content Moderation preset if you enable moderation.
  7. Save.

Usage Tracking and Cost

Every AI request is logged to the wp_jt_pro_ai_usage table with the model used, token counts, latency, and estimated cost.

AI Usage dashboard widget (Pro 1.3.0+) - A dedicated widget appears on the main Jetonomy admin dashboard as soon as at least one provider is connected. It shows at a glance:

  • Requests today and this month
  • Token usage by feature
  • Estimated spend by provider (and percentage of the monthly cap consumed)
  • Average response time
  • Error rate and fallback-chain activations

A detailed breakdown lives at Jetonomy → Settings → AI Integration → Usage with per-day charts, per-feature filtering, and CSV export.

If a feature starts running hot (a new spam wave, an unexpected summary loop), you see it in the dashboard and can pause that feature without disabling the whole extension. The spend cap will also pause the provider automatically if the runaway is expensive enough to cross the cap.

Privacy and Self-Hosting

For communities that cannot send member content to a third-party API - legal, health, financial, enterprise internal - run Ollama on the same server. Jetonomy talks to the model over localhost only. No external network calls, no API keys, no data leaves your machine.

The wp_jt_pro_ai_log audit table records every model decision (feature, object, confidence, action taken) so you have a permanent record of what the AI did and why - useful for compliance reviews.

REST API

The AI extension registers four endpoints under jetonomy/v1:

Method Endpoint Description
POST /ai/test-provider Run a quick ping through the selected provider to confirm credentials
GET /ai/usage Aggregated usage metrics for a date range
POST /ai/summarize/{type}/{id} Request a summary for a post or thread
POST /ai/suggest-reply/{post_id} Request a reply suggestion for a given post

All endpoints require the manage_jetonomy capability.

What's Next?

Give every Pro space its own SEO controls - custom meta titles, Open Graph images, schema, and sitemap rules.

SEO Pro →

Give every space its own meta titles, Open Graph images, Twitter Cards, schema markup, and sitemap rules - without touching your site-wide SEO plugin.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • Why community SEO is different from page/post SEO
  • How to set per-space meta titles and descriptions
  • How to control Open Graph and Twitter Card previews per space
  • How Jetonomy outputs Schema.org structured data automatically
  • How to include or exclude spaces from your sitemap
  • How to customize robots directives and canonical URLs

Why Community SEO Is Different

Your posts and pages have one author, one publish date, and one body. A forum topic has dozens of authors, evolves over time, and the most recent reply is often the most important part. A Q&A thread is really a QAPage with an acceptedAnswer, not a blog post. A sitemap that treats every reply as a top-level URL pollutes your search index.

The free Jetonomy plugin already outputs baseline SEO - canonical URLs, Open Graph tags, Schema.org, and a sitemap provider for spaces and posts. SEO Pro is for when you need to override the defaults on a per-space basis, because different spaces have different audiences, different goals, and different search intent.

Per-Space Meta Title and Description

Every space gets a dedicated SEO tab in its settings panel. Inside that tab:

  • Meta Title - defaults to the space name. Override for search engines only (the on-site title stays unchanged).
  • Meta Description - defaults to the space description. Override with a keyword-rich summary written for search snippets.
  • Meta Keywords - legacy field, not used by Google but some vertical engines still read it. Optional.

Both meta title and description support template tokens: {space_name}, {site_name}, {space_count}, {post_count}. For example, a meta title template of {space_name} - {post_count} discussions | {site_name} produces Python Help - 2,481 discussions | MyForum automatically.

Open Graph and Twitter Cards

SEO Pro lets you upload a custom Open Graph image per space - a 1200x630 image used when the space URL is shared on Facebook, LinkedIn, and Slack. It also lets you override:

  • Open Graph title (defaults to meta title)
  • Open Graph description (defaults to meta description)
  • Twitter Card type (summary or summary_large_image)
  • Twitter Card image

If you skip these fields, Jetonomy falls back to the meta title/description and uses the default OG image from your main settings.

Schema.org Structured Data

For every space and every post, Jetonomy Pro emits JSON-LD structured data:

  • Space pages - DiscussionForumPosting with post count and date
  • Topic pages (Forum) - DiscussionForumPosting with author, date, and answer count
  • Topic pages (Q&A) - QAPage with acceptedAnswer (if marked) and suggestedAnswer entries
  • Topic pages (Ideas) - CreativeWork with about (the idea)
  • User profiles - Person with memberOf the space list
  • Breadcrumbs - BreadcrumbList on every community page

You do not need to configure any of this. Enabling SEO Pro turns it on automatically. The free plugin emits a subset of these (basic DiscussionForumPosting and BreadcrumbList) - SEO Pro adds the richer types.

Sitemap Controls

Jetonomy core registers a sitemap provider for spaces and posts. SEO Pro adds:

  • Include / exclude per space - mark a private support space as excluded from the sitemap with one click
  • Priority per space - boost your flagship space above others in the sitemap XML
  • Change frequency per space - hint at how often a space updates (always, hourly, daily, weekly, monthly)
  • Maximum URLs per sitemap - split large sitemaps into chunks that search engines can crawl without timing out

All sitemap changes take effect on the next request - no flush needed.

Robots Directives and Canonical URLs

  • Robots meta per space - set noindex, nofollow, noarchive, or any combination on a space-by-space basis. Useful for staff-only spaces that should not appear in search results.
  • Custom canonical URL - override the default canonical URL on a space or topic. Useful when you syndicate content from an external blog and want Google to credit the original.
  • robots.txt directives - add SEO Pro specific rules to your site's virtual robots.txt without touching a file.

Enabling SEO Pro

  1. Go to Jetonomy → Extensions and enable SEO Pro.
  2. Open any space in Jetonomy → Spaces.
  3. Click the SEO tab inside the space settings panel.
  4. Fill in the fields you want to override. Leave the rest blank to inherit the site defaults.
  5. Save.

No site-wide settings page - SEO Pro is intentionally per-space, so you do not accidentally change defaults that other spaces depend on.

REST API

Method Endpoint Description
GET /spaces/{id}/seo Get the current SEO settings for a space
PATCH /spaces/{id}/seo Update the SEO settings for a space

Both endpoints require the manage_jetonomy capability or space-admin role.

Compatibility With Site-Wide SEO Plugins

SEO Pro does not replace Yoast, Rank Math, or All in One SEO. It handles the community area only - URLs under /community/ - and leaves your blog, pages, and WooCommerce products entirely alone. If a site-wide SEO plugin already writes og:title for a community URL, SEO Pro's tags take precedence on community pages.

What's Next?

You have now seen every Pro feature. Return to the pro-features overview to pick the extensions that fit your community.

Integrations

Connect with membership plugins, themes, and third-party services.

Connect MemberPress membership levels to Jetonomy spaces - so paying members automatically land in the right discussion areas the moment their subscription activates.

Jetonomy admin settings panel for configuring integrations

What You Will Learn

  • How Jetonomy detects and communicates with MemberPress
  • How to gate a space by membership level using Access Rules
  • What happens when a membership activates or expires
  • How to test the integration before going live

How Detection Works

Jetonomy checks for MemberPress automatically on every page load. No configuration needed. When MemberPress is active, the MemberPress adapter registers itself with Jetonomy's Adapter Registry and enables the Access Rules UI inside each space's settings.

Note: If you activate MemberPress after Jetonomy, navigate to Jetonomy → Settings and save once. This triggers adapter re-registration.

Setting Up an Access Rule

  1. Go to Jetonomy → Spaces and open the space you want to gate.
  2. Click the Access Rules tab in the space settings panel.
  3. Click Add Rule.
  4. Set Rule Type to MemberPress Level.
  5. Select the membership level from the dropdown.
  6. Choose the access action: Grant or Revoke.
  7. Click Save Space.

Members who hold the selected level gain access to this space. Members without it see the space as locked (or hidden, depending on your space visibility setting).

Tip: Combine multiple rules if you want to grant access to more than one membership level. Each rule is evaluated independently - a member passes if they match any Grant rule.

Auto-Join and Auto-Leave

When a MemberPress membership activates, Jetonomy automatically adds the member to any spaces whose Access Rules grant that level. They receive a welcome notification in the community.

When a membership expires, cancels, or is paused, Jetonomy fires jetonomy_membership_deactivated and removes the member from any spaces gated exclusively to that level. Their posts and replies remain intact.

This is handled by the MemberPress_Adapter class, which hooks into MemberPress's mepr-event-transaction-completed and mepr-event-subscription-expired events.

Visibility Behavior

Space Visibility Non-member sees...
Public Space listed, content visible, locked from posting
Private Space listed with lock icon, content hidden
Hidden Space not listed at all

Developer Hook

Both membership events fire the Jetonomy standard hooks you can use in your own code:

// Fires when a MemberPress membership activates.
add_action( 'jetonomy_membership_activated', function( int $user_id, string $level_id, string $adapter ) {
    // $adapter will be 'memberpress'
    if ( 'memberpress' === $adapter ) {
        // Custom logic here.
    }
}, 10, 3 );

Troubleshooting

Access rules dropdown is empty - MemberPress may not be active. Check Plugins → Installed Plugins and confirm MemberPress is activated.

Member not joining on activation - Ensure the membership level ID in the Access Rule exactly matches the level in MemberPress. Level IDs are numeric; check the MemberPress level edit URL for the ID.

Member still has access after expiry - Check whether the member holds a second membership level that also grants access to the space.

What's Next?

Learn how to gate spaces using Paid Memberships Pro, which follows the same pattern.

Paid Memberships Pro Integration →

Gate Jetonomy spaces by Paid Memberships Pro subscription level - with automatic access granted on activation and revoked on cancellation or expiry.

Jetonomy admin settings showing integration configuration options

What You Will Learn

  • How Jetonomy detects Paid Memberships Pro (PMPro)
  • How to set up an Access Rule tied to a PMPro level
  • What triggers the auto-join and auto-leave behavior
  • The hook names you can use for custom logic

How Detection Works

Jetonomy detects PMPro automatically when the plugin is active. The PMPro adapter registers with Jetonomy's Adapter Registry and unlocks the Access Rules tab in space settings. No manual connection step is required.

Note: If you activate PMPro after Jetonomy is already running, go to Jetonomy → Settings and save the page once to trigger adapter re-registration.

Setting Up an Access Rule

  1. Go to Jetonomy → Spaces and open the space you want to gate.
  2. Open the Access Rules tab in the space settings panel.
  3. Click Add Rule.
  4. Set Rule Type to Paid Memberships Pro Level.
  5. Select the PMPro membership level from the dropdown.
  6. Set the action to Grant or Revoke.
  7. Save the space.

Members who hold the selected PMPro level gain access to the space immediately. You can stack multiple rules - access is granted if the member matches any active Grant rule.

Tip: Use a Revoke rule to exclude a specific level from a space, even if that level is granted access by a broader rule.

Auto-Join and Auto-Leave

On activation - when a member's PMPro level activates, Jetonomy adds them to any spaces where that level is in an Access Rule. The hook jetonomy_membership_activated fires with $adapter = 'pmpro'.

On cancellation or expiry - when a PMPro level expires or is manually cancelled, Jetonomy removes the member from gated spaces. The hook jetonomy_membership_deactivated fires with $adapter = 'pmpro'.

The adapter hooks into PMPro's pmpro_after_change_membership_level action to detect both events.

Visibility Behavior

Space Visibility Non-member sees...
Public Space listed, content readable, posting locked
Private Space listed with lock icon, content hidden
Hidden Space not listed at all

Developer Hook

// Fires when a PMPro membership deactivates.
add_action( 'jetonomy_membership_deactivated', function( int $user_id, string $level_id, string $adapter ) {
    if ( 'pmpro' === $adapter ) {
        // Remove user from any related external system.
        my_crm_remove_access( $user_id, $level_id );
    }
}, 10, 3 );

Troubleshooting

Level dropdown is empty in Access Rules - Confirm PMPro is active and that you have at least one membership level created in Memberships → Membership Levels.

Member not removed on cancellation - PMPro has several cancellation states (expired, admin-cancelled, non-renewing). Jetonomy listens to all of them via pmpro_after_change_membership_level where the new level is 0.

Multiple levels, unexpected access - If a user holds more than one PMPro level, Jetonomy evaluates all rules. Access persists as long as any single Grant rule still matches an active level.

What's Next?

Gate spaces using a WooCommerce product purchase or subscription - available in Jetonomy Pro.

WooCommerce Integration →

Gate Jetonomy spaces by WooCommerce product purchase or active WooCommerce Subscription - so customers unlock discussion areas the moment they buy.

Jetonomy admin settings for WooCommerce integration setup

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • Which WooCommerce products Jetonomy Pro supports as access gates
  • How to set up an Access Rule tied to a product or subscription
  • How access activates on purchase and revokes on refund or subscription expiry
  • How to combine product gates with membership-level gates

Supported Product Types

Product Type Supported Notes
Simple product Yes Access granted on order complete, permanent
Variable product Yes Gate by parent product - any variation grants access
WooCommerce Subscriptions Yes Access active while subscription is active; revoked on pause, cancel, or expiry
Grouped product No Gate individual products within the group instead

Note: WooCommerce Subscriptions (the WooCommerce.com extension) is required for subscription-based gating. Simple product gating works with WooCommerce alone.

Setting Up an Access Rule

  1. Install and activate Jetonomy Pro and ensure WooCommerce is active.
  2. Go to Jetonomy → Spaces and open the space you want to gate.
  3. Click the Access Rules tab.
  4. Click Add Rule.
  5. Set Rule Type to WooCommerce Product.
  6. Search for and select the product by name.
  7. Set the action to Grant.
  8. Save the space.

The Access Rule takes effect immediately. Members who have already purchased the product are granted access in the background within a few seconds of saving.

Tip: You can add multiple product rules to a single space. Access is granted if the member has purchased any one of the listed products.

Auto-Activate on Purchase

When a customer's order status reaches Completed, Jetonomy Pro adds them to any spaces that grant access on that product. They receive a Jetonomy notification welcoming them to the space.

If you use WooCommerce Subscriptions, Jetonomy Pro also listens to subscription status changes:

Subscription Status Access
Active Granted
On-hold Revoked
Cancelled Revoked
Expired Revoked
Pending-cancel Retained until expiry date

Auto-Revoke on Refund

When an order is refunded, Jetonomy Pro removes the customer from any spaces gated to that product. The order status transition to Refunded triggers the revocation.

Note: A partial refund does not revoke access. Only a full refund (entire order) triggers the remove. If you need partial-refund gating, use WooCommerce Subscriptions and cancel the subscription manually.

Combining with Other Rules

WooCommerce rules stack with MemberPress, PMPro, and trust-level rules in the same space. A member gains access if they satisfy any Grant rule - regardless of which rule type it is.

Example: gate a "Premium VIP" space to either MemberPress VIP level OR a specific WooCommerce product purchase. Members who qualify through either path are both added automatically.

Troubleshooting

Rule Type dropdown does not show WooCommerce Product - Confirm Jetonomy Pro is active and WooCommerce is active. Navigate to Jetonomy → Extensions and check that the WooCommerce integration is listed.

Subscription product not gating correctly - Confirm WooCommerce Subscriptions (by WooCommerce.com) is installed and active. WooCommerce Memberships (a separate product) is not required.

Member not removed after refund - Verify the order status actually moved to Refunded, not just to Cancelled or On-Hold. Only a full Refunded status triggers access revocation.

What's Next?

Gate spaces by LearnDash course or group enrollment - so students get discussion access automatically when they enroll.

LearnDash Integration →

Connect LearnDash course and group enrollment to Jetonomy spaces - students get dedicated discussion areas automatically when they enroll, and lose access when they un-enroll.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to gate a Jetonomy space by LearnDash course enrollment
  • How to gate a space by LearnDash group membership
  • How to auto-create spaces when new courses are published
  • How to sync existing students into a space
  • What happens when a student un-enrolls

How Detection Works

Jetonomy Pro detects LearnDash automatically when both plugins are active. A LearnDash Course option appears in the Access Rules rule type dropdown - no setup needed. Compatible with LearnDash 4.x and 5.x.

Integrations settings showing active LMS plugins

Gating a Space by Course Enrollment

  1. Go to Jetonomy → Spaces → open the space → Access Rules tab.
  2. Select LearnDash Course from the rule type dropdown.
  3. Start typing the course name - a searchable dropdown shows all published LearnDash courses.
  4. Select the course, set Grants to Participate and Space Role to Member.
  5. Click Add Rule.

The rule appears in the table showing the course name and a Sync Members button.

Gating a Space by LearnDash Group

LearnDash groups also appear in the searchable dropdown. This is ideal for cohort-based learning - one group, one space.

  1. Select LearnDash Course from the rule type dropdown.
  2. Type the group name - groups show as "Group Name (LD Group)" in the results.
  3. Select the group, set Grants and Space Role, and click Add Rule.

All members of the LearnDash group - including group leaders - gain access. When a user is removed from the group, they lose space access.

Syncing Existing Students

If students are already enrolled before the rule was created, click the Sync Members button. This pulls in all currently enrolled users. A notification shows how many were synced.

New enrollments and removals are handled automatically after the rule is created.

Auto-Create Spaces for New Courses

  1. Go to Jetonomy → Settings → Integrations.
  2. Enable the LearnDash toggle under Auto-Create Spaces for Courses.
  3. Choose the default space type (Q&A, Forum, or Feed).
  4. Click Save Settings.

When you publish a new course in LearnDash, a private discussion space is automatically created with:

  • The course title as the space name
  • A membership access rule linking the course to the space
  • The course author assigned as space admin

Enrollment and Un-enrollment Events

LearnDash Event Jetonomy Action
Student enrolls in course Added to linked spaces as Member
Student completes course Access retained
Student un-enrolls or is removed Removed from linked spaces
User added to group Added to linked spaces as Member
User removed from group Removed from linked spaces

Content (posts and replies) remains in the space - only access is revoked.

Typical Setup for a Course Community

  • One Private space per course, gated to that course's enrollment
  • One Public space for general Q&A open to all students
  • One Hidden space per group/cohort, gated to a LearnDash Group

Troubleshooting

LearnDash Course does not appear in the rule type dropdown - Confirm Jetonomy Pro and LearnDash are both active. Check Jetonomy → Settings → Integrations to see the LearnDash status.

Students still have access after un-enrolling - Confirm the un-enrollment uses LearnDash's standard course access management. Custom enrollment plugins that bypass the learndash_update_course_access hook will not trigger removal.

Sync Members shows 0 synced - The students may already be space members, or no users are enrolled in the selected course.

What's Next?

Learn how to gate spaces using Restrict Content Pro subscriptions.

Restrict Content Pro Integration →

Gate Jetonomy spaces by Restrict Content Pro subscription level - with automatic access on activation and automatic removal on expiry or cancellation.

Jetonomy admin settings for Restrict Content Pro integration

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How Jetonomy Pro detects Restrict Content Pro (RCP)
  • How to create an Access Rule tied to an RCP subscription level
  • What membership events trigger auto-join and auto-leave
  • How to handle free and paid RCP levels differently

How Detection Works

Jetonomy Pro detects Restrict Content Pro automatically when both plugins are active. The RCP adapter registers with the Adapter Registry and adds Restrict Content Pro Level as a Rule Type in the Access Rules tab for every space.

Note: Jetonomy Pro supports Restrict Content Pro version 3.x and above. If you are on an older version, update RCP first.

Setting Up an Access Rule

  1. Go to Jetonomy → Spaces and open the space you want to gate.
  2. Click the Access Rules tab.
  3. Click Add Rule → set Rule Type to Restrict Content Pro Level.
  4. Select the subscription level from the dropdown.
  5. Set the action to Grant.
  6. Save the space.

Members with an active subscription to the selected level gain access immediately. You can add multiple rules to grant access across more than one RCP level.

Tip: RCP supports free membership levels. You can use a free level as a gate to require a (free) registration before members can post - while still keeping the community open to anyone willing to sign up.

Auto-Join on Activation

When a member's RCP subscription status becomes active, Jetonomy Pro adds them to any spaces that grant that subscription level. The standard Jetonomy hook fires:

add_action( 'jetonomy_membership_activated', function( int $user_id, string $level_id, string $adapter ) {
    // $adapter is 'rcp' for Restrict Content Pro events.
}, 10, 3 );

Auto-Leave on Expiry or Cancellation

When an RCP subscription expires, is cancelled, or is set to pending, Jetonomy Pro removes the member from any spaces gated exclusively to that level. The hook jetonomy_membership_deactivated fires with $adapter = 'rcp'.

RCP Status Access
Active Granted
Pending Revoked
Expired Revoked
Cancelled Revoked
Disabled Revoked

Combining with Other Adapters

RCP rules stack with all other Access Rule types in Jetonomy. A member gains access to a space if they satisfy any single Grant rule - whether it comes from RCP, MemberPress, WooCommerce, or trust level.

Troubleshooting

RCP Level not appearing in Rule Type dropdown - Confirm Jetonomy Pro is active and Restrict Content Pro is active. Check that at least one subscription level exists in Restrict Content → Subscription Levels.

Member not removed after subscription expires - RCP can be configured to change status on expiry, grace periods, or manual review. Jetonomy listens to the rcp_set_status action. If a custom RCP workflow bypasses this action, auto-removal will not fire.

Access still active after cancellation - Check whether the user holds a second active RCP subscription that also grants access to the space via a separate rule.

What's Next?

Learn how Jetonomy integrates with BuddyNext for a unified community hub experience.

BuddyNext Integration →

When BuddyNext is active alongside Jetonomy, the two plugins integrate automatically - sharing navigation, design tokens, and community surfaces without any configuration.

Jetonomy admin settings showing BuddyNext integration status

What You Will Learn

  • What the BuddyNext integration enables
  • How shared navigation and design tokens work
  • How Jetonomy surfaces in BuddyNext's community hub
  • What you need to do (spoiler: nothing)

Auto-Detection

Jetonomy checks for BuddyNext on every load via the buddynext_loaded action. When detected, the integration layer activates automatically. There are no settings to configure and no toggles to enable.

Note: The integration activates only when BuddyNext 1.0 or higher is active and the BuddyNext community hub feature is enabled.

Shared Design Tokens

BuddyNext's TokenService injects CSS custom properties (--brand, --bg, --text-1, --green, --amber, --red, --r-md, --font-body, --font-display) into the page. Jetonomy's CSS inherits these automatically through its --jt-* token cascade:

--jt-accent: var(--brand, var(--wp--preset--color--primary, #3B82F6));
--jt-bg:     var(--bg,    var(--wp--preset--color--base,    #ffffff));
--jt-font:   var(--font-body, var(--wp--preset--font-family--body, inherit));

This means Jetonomy matches the BuddyNext visual style without you writing a single line of CSS. Accent color, typography, border radius, and dark mode all flow through automatically.

Forum Tab in BuddyNext Spaces

When BuddyNext community spaces are active, Jetonomy adds a Forum tab to each BuddyNext space. The tab links to the corresponding Jetonomy space, filtered to show only posts from that community space.

BuddyNext uses the jetonomy_template_map filter to register the forum tab route. This integration is non-destructive - if a BuddyNext space has no linked Jetonomy space, the Forum tab does not appear.

Unified Navigation

Jetonomy's community header navigation respects BuddyNext's active navigation state. When a user is inside a BuddyNext community, the Jetonomy breadcrumb trail includes the BuddyNext community name as the first crumb.

The jetonomy_profile_url filter is also overridden automatically: user profile links inside Jetonomy point to BuddyNext member profiles instead of Jetonomy's built-in /community/u/ pages.

Dark Mode

BuddyNext's dark mode toggle sets data-theme="dark" on the document root. Jetonomy's dark mode overrides live in .jt-dark .jt-app and respond to this same attribute - so toggling dark mode in BuddyNext also applies to Jetonomy community pages instantly.

Developer Notes

If you are building a custom BuddyNext integration, the BuddyNext bridge code lives in includes/adapters/class-buddynext-bridge.php in the Jetonomy plugin. You can hook into:

// Fires after BuddyNext integration initializes.
do_action( 'jetonomy_buddynext_ready' );

Use this hook to register additional tab mappings or extend the forum tab with custom fields.

Troubleshooting

Forum tab not appearing in BuddyNext spaces - Confirm BuddyNext's community spaces feature is active (not just the plugin). Also confirm the BuddyNext space has a linked Jetonomy space configured in its space settings.

Design tokens not matching - BuddyNext's TokenService may not be firing on community pages. Check the BuddyNext community page template and ensure buddynext_loaded action fires before the wp_head hook.

What's Next?

Learn how Jetonomy adapts to any WordPress theme via CSS custom properties and template overrides.

Theme Compatibility →

Jetonomy works with any WordPress theme. Its CSS inherits from your theme's design tokens automatically, so the community looks native - not bolted on.

Community home page adapting to the active WordPress theme

What You Will Learn

  • How Jetonomy adapts its visual style to your active theme
  • What zero-config means with BuddyX
  • How to override community templates from your theme
  • How to control design tokens for fine-grained customization

How Theme Adaptation Works

Jetonomy reads WordPress theme.json values through CSS custom properties. Every --jt-* token in Jetonomy's CSS resolves first to a WordPress preset token (--wp--preset--color--primary, --wp--preset--font-family--body, etc.), with a hardcoded fallback last:

--jt-accent: var(--brand, var(--wp--preset--color--primary, #3B82F6));
--jt-text:   var(--text-1, var(--wp--preset--color--contrast, #1a1a1a));
--jt-font:   var(--font-body, var(--wp--preset--font-family--body, inherit));
--jt-radius: var(--r-md, var(--wp--custom--border-radius, 8px));

If your theme defines these standard WP preset tokens, Jetonomy adopts the theme's colors, typography, and spacing without you writing any CSS.

Best With BuddyX

BuddyX is Jetonomy's reference theme. With BuddyX active, Jetonomy requires zero configuration - colors, fonts, border radius, hover states, and dark mode all match the theme perfectly out of the box.

Tip: If you are building a new community site from scratch, start with BuddyX. You can always switch themes later - Jetonomy will adapt.

BuddyX Pro, Reign, and the Theme Bridge (1.3.0+)

Starting in 1.3.0, Jetonomy ships a dedicated bridge for the three Kirki-based themes most of our customers run: BuddyX, BuddyX Pro, and Reign.

What the bridge does

  • Reads the theme's Kirki mods on every render (accent color, dark mode state, container width).
  • Injects --jt-accent directly so the community picks up the exact color the customer chose in the Customizer - not a hardcoded fallback.
  • Toggles .jt-dark on the page <body> via body_class whenever the theme is in dark mode, so the community's dark overrides activate automatically without requiring custom CSS.
  • No configuration screen. If the theme is active, the bridge runs. If you switch to a non-Kirki theme, the bridge silently bows out and the standard theme.json path takes over.

Where it lives

includes/integrations/class-theme-integration.php - guarded by class_exists( 'Kirki' ) and a per-theme check against the theme template slug.

Why this matters

On BuddyX/BuddyX Pro/Reign, flipping the theme's dark-mode toggle in the Customizer now flips the entire community sidebar, nav, post cards, and reply editor in the same render. No custom-CSS bridge required.

If you build a custom Kirki theme and want to hook into the same bridge, the integration is extensible via the jetonomy_theme_integration_accent and jetonomy_theme_integration_dark_mode filters - return your own values and Jetonomy will use them.

Using Other Themes

Jetonomy works with any well-built WordPress theme. Compatibility level depends on how fully the theme uses theme.json:

Theme Type Expected Result
Modern block theme (theme.json) Excellent - tokens inherit fully
Classic theme with CSS variables Good - accent and font tokens pick up if variable names match
Classic theme without CSS variables Functional - Jetonomy falls back to its own neutral defaults

For classic themes, you can override the --jt-accent token in your theme's style.css or via Jetonomy → Settings → Appearance → Custom CSS.

Dark Mode

Jetonomy supports dark mode natively. If your theme sets data-theme="dark" or a .dark class on the <html> or <body> element, Jetonomy's dark overrides activate automatically via the .jt-dark .jt-app CSS selector.

BuddyX and BuddyNext set data-theme="dark" - so dark mode is seamless. For other themes, add a small bridge if their dark mode uses a different selector:

/* In your theme's style.css - bridge for custom dark mode selector */
.my-theme-dark .jt-app { --jt-bg: #121212; --jt-text: #f0f0f0; }

Template Overrides

Jetonomy's frontend is powered by PHP templates. Every template can be overridden in your theme without touching plugin files.

Create a jetonomy/ folder inside your theme and copy any template file from wp-content/plugins/jetonomy/templates/ into it. Jetonomy's template loader checks the theme directory first.

Override directory structure:

your-theme/
└── jetonomy/
    ├── views/
    │   ├── community-home.php
    │   ├── space.php
    │   ├── single-post.php
    │   └── user-profile.php
    └── partials/
        ├── header.php
        └── reply-card.php

Note: When you override a template, you own it. Future Jetonomy updates will not touch your override. Check the plugin's template changelog after each update to merge any changes manually.

Customizing Design Tokens Without Template Overrides

For small visual adjustments, use the Custom CSS field in Jetonomy → Settings → Appearance. You can re-define any --jt-* token at the .jt-app scope:

.jt-app {
    --jt-accent: #e11d48;
    --jt-radius: 4px;
}

This approach is update-safe and does not require template overrides.

Available --jt-* Tokens

Category Tokens
Typography --jt-font, --jt-font-heading, --jt-font-mono
Accent --jt-accent, --jt-accent-hover, --jt-accent-light, --jt-accent-muted
Text --jt-text, --jt-text-secondary, --jt-text-tertiary
Background --jt-bg, --jt-bg-subtle, --jt-bg-muted, --jt-bg-hover
Border --jt-border, --jt-border-strong
Semantic --jt-success, --jt-warn, --jt-danger and their -light variants
Radius --jt-radius, --jt-radius-sm, --jt-radius-lg, --jt-radius-full

What's Next?

Configure your community's global settings - URL slug, pagination, and access defaults.

General Settings →

Connect Tutor LMS course enrollment to Jetonomy spaces - students get a dedicated discussion area automatically when they enroll, and lose access when they cancel or are removed.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to gate a Jetonomy space by Tutor LMS course enrollment
  • How to auto-create spaces when new courses are published
  • How to sync existing students into a space
  • What happens when a student un-enrolls or cancels

How Detection Works

Jetonomy Pro detects Tutor LMS automatically when both plugins are active. A Tutor Course option appears in the Access Rules rule type dropdown - no setup needed.

Integrations settings showing Tutor LMS active

Linking a Course to a Space

  1. Go to Jetonomy → Spaces and open (or create) the discussion space for your course.
  2. Open the Access Rules tab.
  3. Select Tutor Course from the rule type dropdown.
  4. Start typing your course name - a searchable dropdown appears showing all published Tutor courses.
  5. Select the course, set Grants to Participate and Space Role to Member.
  6. Click Add Rule.

The rule appears in the table showing the course name (not an ID), with a Sync Members button and Delete button.

Access rules with Tutor Course rule and Sync Members button

Searching for a Tutor course in the autocomplete dropdown

Syncing Existing Students

If students are already enrolled in the course before the rule was created, click the Sync Members button next to the rule. This pulls in all currently enrolled students and adds them to the space. A toast notification shows how many were synced.

New enrollments and cancellations are handled automatically after the rule is created - no further action needed.

Auto-Create Spaces for New Courses

Instead of manually creating a space for each course:

  1. Go to Jetonomy → Settings → Integrations.
  2. Under Auto-Create Spaces for Courses, enable the Tutor LMS toggle.
  3. Choose the default space type (Q&A, Forum, or Feed).
  4. Click Save Settings.

Now when you publish a new course in Tutor, a private discussion space is automatically created with:

  • The course title as the space name
  • A membership access rule linking the course to the space
  • The course author assigned as space admin

Enrollment and Un-enrollment Events

Tutor LMS Event Jetonomy Action
Student enrolls in course Added to linked space as Member
Student completes course Access retained
Student enrollment cancelled Removed from linked space
Student enrollment deleted Removed from linked space

Content (posts and replies) created by the student remains in the space - only access is revoked.

Typical Setup

  • One Private space per paid course, gated to enrollment
  • One Public space per free course for open discussion
  • One Public space for general Q&A open to all students

Troubleshooting

Tutor Course does not appear in the rule type dropdown - Confirm Jetonomy Pro and Tutor LMS are both active. Check Jetonomy → Settings → Integrations to see the Tutor LMS status.

Students still have access after cancellation - Confirm the cancellation uses Tutor's standard enrollment management. Set the space to Private to fully restrict access.

Sync Members shows 0 synced - The students may already be space members, or no users are enrolled in the selected course.

What's Next?

LifterLMS Integration →

Connect LifterLMS course and membership enrollment to Jetonomy spaces - students get a dedicated discussion area when they enroll, and lose access when removed.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to gate a space by LifterLMS course or membership enrollment
  • How to auto-create spaces when new courses are published
  • How to sync existing students into a space

How Detection Works

Jetonomy Pro detects LifterLMS automatically when both plugins are active. A LifterLMS Course option appears in the Access Rules rule type dropdown.

Integrations settings showing active LMS plugins

Linking a Course to a Space

  1. Go to Jetonomy → Spaces → open the space → Access Rules tab.
  2. Select LifterLMS Course from the rule type dropdown.
  3. Start typing the course name - a searchable dropdown shows all published courses and memberships.
  4. Select the course, set Grants to Participate and Space Role to Member.
  5. Click Add Rule.

LifterLMS memberships also appear in the search - select a membership to gate a space by membership level instead of individual course enrollment.

Syncing Existing Students

Click the Sync Members button next to any rule to pull in all currently enrolled students. New enrollments and removals sync automatically.

Auto-Create Spaces for New Courses

  1. Go to Jetonomy → Settings → Integrations.
  2. Enable the LifterLMS toggle under Auto-Create Spaces for Courses.
  3. Choose the default space type and save.

When you publish a new LifterLMS course, a private space is created with the course name, an access rule, and the course author as space admin.

Enrollment and Un-enrollment Events

LifterLMS Event Jetonomy Action
Student enrolls in course Added to linked space as Member
Student added to membership Added to linked space as Member
Student completes course Access retained
Student removed from course Removed from linked space
Student removed from membership Removed from linked space
Enrollment permanently deleted Removed from linked space

Content remains in the space - only access is revoked.

Memberships and Courses

LifterLMS memberships can auto-enroll students in multiple courses. When a student joins a membership:

  • They gain access to spaces linked to the membership itself
  • They also gain access to spaces linked to the auto-enrolled courses (via the course enrollment hooks)

Troubleshooting

LifterLMS Course does not appear in dropdown - Confirm Jetonomy Pro and LifterLMS are both active. Check Jetonomy → Settings → Integrations.

Membership students not getting access - Ensure the access rule uses the membership level ID (shows as "Membership Name (LifterLMS Membership)" in the search). Course-level rules only apply to direct course enrollment.

What's Next?

Sensei LMS Integration →

Connect Sensei LMS course enrollment to Jetonomy spaces - students get a dedicated discussion area when enrolled, and lose access when withdrawn.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to gate a space by Sensei LMS course enrollment
  • How to auto-create spaces when new courses are published
  • How to sync existing students into a space

How Detection Works

Jetonomy Pro detects Sensei LMS automatically when both plugins are active. A Sensei Course option appears in the Access Rules rule type dropdown.

Integrations settings showing active LMS plugins

Linking a Course to a Space

  1. Go to Jetonomy → Spaces → open the space → Access Rules tab.
  2. Select Sensei Course from the rule type dropdown.
  3. Start typing the course name - a searchable dropdown shows all published Sensei courses.
  4. Select the course, set Grants to Participate and Space Role to Member.
  5. Click Add Rule.

Syncing Existing Students

Click the Sync Members button next to any rule to pull in all currently enrolled learners. New enrollments and withdrawals sync automatically.

Auto-Create Spaces for New Courses

  1. Go to Jetonomy → Settings → Integrations.
  2. Enable the Sensei LMS toggle under Auto-Create Spaces for Courses.
  3. Choose the default space type and save.

When you publish a new Sensei course, a private space is created with the course name, an access rule, and the course author (teacher) as space admin.

Enrollment and Un-enrollment Events

Sensei uses a single enrollment status change event that handles both enrollment and withdrawal:

Sensei LMS Event Jetonomy Action
Learner enrolled in course Added to linked space as Member
Learner completes course Access retained
Learner withdrawn from course Removed from linked space
Learner manually enrolled by admin Added to linked space as Member
Learner manually withdrawn by admin Removed from linked space

Content remains in the space - only access is revoked.

WooCommerce Integration

Sensei LMS integrates with WooCommerce for paid courses. When a student purchases a course through WooCommerce and Sensei enrolls them, the Jetonomy space access rule triggers automatically - no additional WooCommerce adapter needed for course gating.

Troubleshooting

Sensei Course does not appear in dropdown - Confirm Jetonomy Pro and Sensei LMS are both active. Check Jetonomy → Settings → Integrations.

Students not losing access after withdrawal - Ensure the withdrawal uses Sensei's standard enrollment management. Set the space to Private to fully restrict access.

What's Next?

MasterStudy LMS Integration →

Connect MasterStudy LMS course enrollment to Jetonomy spaces - students get a dedicated discussion area when they enroll in a course.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • How to gate a space by MasterStudy LMS course enrollment
  • How to auto-create spaces when new courses are published
  • How to sync existing students into a space

How Detection Works

Jetonomy Pro detects MasterStudy LMS automatically when both plugins are active. A MasterStudy Course option appears in the Access Rules rule type dropdown.

Integrations settings showing active LMS plugins

Linking a Course to a Space

  1. Go to Jetonomy → Spaces → open the space → Access Rules tab.
  2. Select MasterStudy Course from the rule type dropdown.
  3. Start typing the course name - a searchable dropdown shows all published MasterStudy courses.
  4. Select the course, set Grants to Participate and Space Role to Member.
  5. Click Add Rule.

Syncing Existing Students

Click the Sync Members button next to any rule to pull in all currently enrolled students. New enrollments sync automatically.

Auto-Create Spaces for New Courses

  1. Go to Jetonomy → Settings → Integrations.
  2. Enable the MasterStudy LMS toggle under Auto-Create Spaces for Courses.
  3. Choose the default space type and save.

When you publish a new MasterStudy course, a private space is created with the course name, an access rule, and the course author as space admin.

Enrollment Events

MasterStudy LMS Event Jetonomy Action
Student enrolls in course Added to linked space as Member
Student completes course Access retained

Important Note on Removal

MasterStudy LMS does not fire a hook when students are removed from courses. This means automatic removal from linked spaces is not available. To manage access:

  • Use the Sync Members button periodically to re-sync - students no longer enrolled will not be re-added
  • Remove students manually from the space Members tab
  • For subscription-based access, consider pairing MasterStudy with WooCommerce Memberships which does fire removal hooks

Troubleshooting

MasterStudy Course does not appear in dropdown - Confirm Jetonomy Pro and MasterStudy LMS are both active. Check Jetonomy → Settings → Integrations.

Students remain in space after course access revoked - MasterStudy does not fire removal hooks. Remove students manually from the space Members tab or use the Sync Members button to verify current enrollment.

What's Next?

BuddyNext Integration →

When FluentCommunity is active alongside Jetonomy, the two plugins coexist as one product. Members navigate between the social feed and the forum without noticing two separate systems, and admins pair spaces in one click.

Jetonomy admin settings showing the FluentCommunity integration tab

What You Will Learn

  • How the integration auto-detects FluentCommunity
  • How to pair FluentCommunity spaces with Jetonomy spaces
  • How member sync, activity broadcast, and the comment-to-reply bridge work
  • How the cross-profile links and unified avatars behave
  • What stays separate (private content, moderation, notifications)

Auto-Detection

Jetonomy checks for FluentCommunity on every load via class_exists( 'FluentCommunity\\App\\App' ). When detected, the integration layer activates automatically. There are no toggles to flip and no bootstrap code to add.

The integration is read-only toward FluentCommunity's database: every write goes through FluentCommunity's own helpers (addToSpace) and the Feed model, never via direct SQL. Deactivate FluentCommunity at any time and both plugins continue to work independently.

Pairing Spaces

Open Jetonomy Settings > FluentCommunity. You will see:

  • A status line confirming FluentCommunity is detected
  • A tab-label field (default "Discussions")
  • A pairings table where each row maps one FluentCommunity space to one Jetonomy space

Click Add pair, pick the FluentCommunity space on the left and the Jetonomy space on the right, and save. The pairing is stored in a single WordPress option (jetonomy_fc_space_pairs). Unpair a row by setting the Jetonomy column to "Not paired".

Once a pair is saved, you get five connected surfaces automatically:

  1. A Discussions tab appears on the FluentCommunity space header, linking to the paired Jetonomy space.
  2. An Also on {your community} card appears in the Jetonomy space sidebar, linking back to the FluentCommunity feed.
  3. The FluentCommunity profile gets a Discussions block showing the member's five most recent topics started plus the five topics they follow on Jetonomy.
  4. The Jetonomy profile gets a View on {your community} cross-link to the member's FluentCommunity profile.
  5. Member avatars unify across both sides: the FluentCommunity avatar is used everywhere on the site, including Jetonomy pages.

The tab, card, and button labels pick up the Site Title configured in FluentCommunity so the wording matches your community brand. The Discussions label is configurable from the settings page.

Member Sync

When a member joins a paired FluentCommunity space, they are automatically added to the paired Jetonomy space. The reverse also holds: joining the Jetonomy space enrols the member in the FluentCommunity space.

Sync is add-only by design:

  • Joins propagate in both directions.
  • Leaves do not propagate. Removing yourself from one side never yanks you out of the other.
  • Role changes do not propagate. Each plugin manages its own role structure.

Member sync is enabled by default and can be toggled off from the settings page. There is also a Sync existing members now button that performs a one-click backfill: every member already in one side of a pair is enrolled into the other side. Backfill is safe to re-run and capped at 5,000 members per space per run.

Activity Broadcast

When a new topic is created in a paired Jetonomy space, an announcement feed post appears in the paired FluentCommunity space with the topic title, excerpt, and a discreet "Shared from the forum" attribution link.

Properties:

  • One-way only. Broadcast runs from Jetonomy to FluentCommunity. FluentCommunity feed posts never silently create forum topics.
  • Private topics are never broadcast. If a topic is marked private in Jetonomy, nothing is posted to FluentCommunity. The FC feed audience can be broader than the private-topic scope, so private content stays where it was authored.
  • Paragraph breaks preserved. The excerpt keeps its formatting through the HTML-to-plain conversion.
  • No duplicated titles. The feed post uses the topic title as its heading once, with the excerpt as body and a footer link back to the Jetonomy topic.

Broadcast is enabled by default and can be toggled off from the settings page.

Comment-to-Reply Bridge

When a member comments on one of the broadcast feed posts in FluentCommunity, the comment is mirrored back as a reply on the original Jetonomy topic, preserving author attribution.

Only comments on broadcast feed posts round-trip. Native FluentCommunity feed posts (ones Jetonomy did not broadcast) are left alone. Edits and deletes on FluentCommunity do not propagate: the forum thread remains the durable record of record.

Cross-Profile Navigation

Navigation between the two profile systems works in both directions:

  • FluentCommunity profile: the Discussions block ends with a View all on forum link pointing to the Jetonomy profile.
  • Jetonomy profile: a View on {your community} button points to the FluentCommunity profile.

The FluentCommunity site title is read from the fluent_community_settings option's site_title key, so the button text always matches what your FC admin has branded the community with. If you have not set one, the button falls back to your WordPress site name, then to "Community".

Identity Keying

Everything joins on user_id, never on username. This matters when a user has different usernames on the two sides (for example WP user admin with FluentCommunity xprofile username admin2). user_id is the primary key in both wp_fcom_xprofile and wp_jt_user_profiles, so the integration stays correct no matter how usernames diverge.

Stability Guarantees

  • No core changes to FluentCommunity or Jetonomy. Everything lives in one integration file in the Jetonomy plugin.
  • No writes to FluentCommunity tables outside FC's public helpers. Remove the integration and FluentCommunity is untouched.
  • Stale pair handling. If either side's space ID does not resolve (deleted space, renamed slug), the tab and card silently disappear. No admin cleanup needed.
  • Graceful degradation. If a FluentCommunity hook is ever renamed or removed in a future FC release, only the corresponding surface stops rendering. The rest of the integration keeps working.
  • One option row. The pairing map is stored in a single WordPress option. Uninstall deletes one row.

What Is Not Integrated (Yet)

Kept out of the first release on purpose:

  • Leave sync. Destructive semantics: pulling someone off one side when they leave the other is too likely to cause surprise removals. Add-only stays the default.
  • Privacy mirroring. Paired spaces do not share visibility settings. Exposing or hiding content across two plugins silently is a product decision, not a hook decision.
  • Profile field sync. Bios, display names, and custom fields are not synced two-way.
  • Notification inbox merge. Each plugin keeps its own notification stream.
  • Unified search. FC and Jetonomy have different search surfaces; querying both simultaneously is not wired.
  • Shared moderation queue. Different content models, different flag rules.

Open a request if one of these is the feature you need. We ship what customers actually use.


Building on top of the integration? See the FluentCommunity integration reference in the Developer Guide for options, hooks, and extension points.

When BuddyPress is active alongside Jetonomy, groups and forum spaces feel like one membership. Members who join a group are enrolled in the paired forum space, new topics are announced on the group activity stream, and comments on those activities flow back to the topic as replies.

Jetonomy admin settings with the BuddyPress integration active

What You Will Learn

  • How the integration auto-detects BuddyPress
  • How to pair a group with a forum space (create a new one or link an existing one)
  • How member sync and role sync work across groups and spaces
  • How topics broadcast to the group activity stream and how comments round-trip back as replies
  • What surfaces appear on group pages and member profiles

Auto-Detection

Jetonomy checks for BuddyPress's Groups component on every load via bp_is_active( 'groups' ). When the component is active, the integration layer activates automatically. No toggle to flip, no bootstrap code to add.

The broadcast and comment-bridge parts also check the Activity component at runtime, so a BuddyPress install with Groups enabled but Activity disabled keeps the rest of the integration working and silently skips the activity pieces.

Pairing Groups and Spaces

Group-to-space pairs are set directly inside BuddyPress's group creation wizard and the group Manage > Details screen. The Jetonomy integration adds a Discussion Forum section with three choices:

  • No forum. The group has no paired forum space. Default for new groups.
  • Create new discussion forum. Creates a matching Jetonomy space with the group's name, description, and visibility (public → public, private → private, hidden → hidden). The group creator becomes the space admin.
  • Link existing forum. Pick from a dropdown of unlinked spaces the current user already owns or moderates. Site admins see every unlinked space.

The pairing is stored as jetonomy_space_id in BuddyPress's group meta (one value per group). Unpairing a group is as simple as setting the dropdown back to "No forum".

Member Sync

Once a group is paired, membership changes propagate both ways:

  • Join a BP group → added to the paired Jetonomy space as a member.
  • Join the paired Jetonomy space → added to the BP group as a member.
  • Leave or be removed from the BP group → removed from the paired space.
  • Banned from the group → removed from the space.
  • Unbanned → re-added to the space.
  • Promoted to group admin → space admin.
  • Promoted to group moderator → space moderator.
  • Demoted → back to space member.

Unlike the FluentCommunity integration, BuddyPress member sync is bidirectional for both adds and leaves. Groups and forum spaces are treated as a single membership, which is the model most BuddyPress sites expect. If your site needs add-only semantics, remove groups_leave_group, groups_remove_member, and groups_ban_member from the BuddyPress integration at a hook-filter level.

Forum Tab on BP Group Pages

Paired groups gain a Forum tab in their group nav. Clicking it renders a list of the most recent topics in the paired space with reply counts, author, and time-ago, plus a + New Topic button for signed-in members.

The listing is visibility-aware: private topics only appear to the author or a moderator, so activity-stream widgets and group-page readers never see content they shouldn't.

Forum Nav on BP Member Profiles

Every member profile gets a Forum primary nav item with three sub-tabs:

  • Posts: the member's most recent topics with reply counts and links.
  • Replies: the member's most recent replies with links to the original topics.
  • Bookmarks: topics the member has bookmarked. Own profile only; others don't see your bookmarks.

A stats block above each sub-tab shows the member's posts, replies, votes, reputation, and trust level at a glance. A View Full Forum Profile link takes them to the Jetonomy profile for deeper history.

Back-to-Group Banner on Jetonomy Pages

When a member clicks through from a BP group to a paired Jetonomy space (or a topic in it), a subtle back-link banner at the top of the Jetonomy page lets them return to the group with one click. Keeps the navigation continuous.

Sidebar: Linked Group

On paired Jetonomy spaces, the sidebar About card shows a small tag linking to the paired BP group. Useful for members who enter the space from the forum side and want to jump back to the group's activity feed or members list.

Activity Broadcast (New)

When a new topic is created in a paired Jetonomy space, an activity item is posted to the paired BP group's activity stream with:

  • An action line: "Someone started a new forum topic in Group Name" (rendered by BuddyPress with the standard member + group links).
  • The topic excerpt as paragraphs.
  • A discreet "Shared from the forum · View discussion" attribution line at the bottom.

Properties:

  • One-way only. Broadcast runs from Jetonomy to BuddyPress. BP activity posts never silently create forum topics.
  • Private topics are never broadcast. If a topic is marked private on the Jetonomy side, no activity item is created. The group audience can be broader than the private-topic scope on the forum side.
  • Private/hidden groups stay private. The activity is posted with hide_sitewide set, so site-wide activity feeds do not leak the item outside the group.
  • Paragraph breaks preserved. The excerpt keeps its structure because the integration whitelists <br> and <p> on bp_activity_allowed_tags for broadcast content (tags are harmless on their own, with no attributes allowed through).

Broadcast is enabled by default and can be turned off via the jetonomy_bp_broadcast option.

Comment-to-Reply Bridge (New)

When a member comments on one of those broadcast activity items, the comment is mirrored back to the originating Jetonomy topic as a reply, with author attribution preserved.

Only comments on broadcast activities round-trip. Comments on native BP activity posts (status updates, other plugins' activity types) are left alone. The integration identifies broadcast activities by a jetonomy_post_id activity-meta marker it sets at post time.

Add-only by design: edits and deletes on the BuddyPress side do not propagate back. The forum thread stays the durable record. Enabled by default; toggle via jetonomy_bp_comment_bridge.

Identity Keying

Everything joins on user_id, which is the same primary key for BuddyPress user profiles and Jetonomy user profiles. Usernames and display names can diverge between the two without breaking the integration.

Stability Guarantees

  • No core changes to BuddyPress. Everything lives in Jetonomy's integration class; BP tables are read via its public API.
  • Group meta is the only data footprint on the BP side (one jetonomy_space_id key per paired group). Deactivating Jetonomy leaves BuddyPress untouched.
  • Graceful degradation. If BuddyPress is removed, the BP-specific surfaces disappear (the integration class is gated on bp_is_active('groups')), and the paired Jetonomy spaces continue to work on their own.
  • Activity component optional. The broadcast and comment bridge gate themselves on bp_is_active('activity') at runtime, so sites with BP Groups but not Activity still get member sync and forum tabs.
  • Stale pair handling. If a paired space is deleted on the Jetonomy side, the forum tab on the BP group quietly disappears on next render. No admin cleanup required, no fatal.

What Is Not Integrated (Yet)

  • Cross-plugin notification merge. BuddyPress notifications and Jetonomy notifications live in separate inboxes. Members see them in two places.
  • Unified search. Each plugin has its own search UX.
  • Shared moderation queue. Group moderation and forum moderation are independent. A user banned from a group is removed from the space but not flagged for forum moderation, and vice versa.
  • Two-way edit/delete sync on comments and replies. The comment bridge creates a reply on round-trip but does not keep the reply and the activity comment in sync after that point.

Building on top of the integration? See the BuddyPress integration reference in the Developer Guide for hooks, options, and extension points.

Admin Settings

Configure every aspect of your Jetonomy community.

The General settings tab is the first place to go after installation. It controls your community URL, pagination defaults, and who can read or participate.

What You Will Learn

  • How to change your community's base URL slug
  • What the default space type setting controls
  • How to configure pagination for posts and replies
  • How guest access and login requirements work

General settings

Go to Jetonomy → Settings to access these options. All changes take effect on save.

Community Base URL

Setting: base_slug Default: community Location: General tab → Community URL section

This is the URL prefix for all Jetonomy pages on your site. With the default value, your community home is at yoursite.com/community/. Spaces live at yoursite.com/community/s/space-name/, and so on.

You can change this to any URL-safe string, for example forum, hub, or discuss. Jetonomy automatically flushes rewrite rules when you change the base URL and save settings.

Warning: Changing the base URL after your community has content will break all existing links. If you must change it on a live site, set up 301 redirects from the old slug to the new one.

Default Space Type

Setting: default_space_type Default: forum Options: Forum, Q&A, Ideas, Show & Tell

When you create a new space in the admin, this setting pre-fills the Type dropdown. It is a convenience setting only - you can always change the type on any individual space. It does not affect existing spaces.

Choose the type that best matches the primary purpose of your community:

  • Forum - open discussion, replies sorted by date
  • Q&A - questions and answers, accepted answers float to top
  • Ideas - feature requests and votes, status workflow
  • Show & Tell - short-form cards, optional title, chronological feed

Posts Per Page

Setting: posts_per_page Default: 20 Location: General tab → Pagination section

Controls how many posts appear per page in space listings and search results. A lower number is faster on large communities; a higher number reduces clicks for users who prefer scrolling. This value also controls how many additional posts load each time a member clicks Load More on a space listing.

Tip: For communities with 10,000+ posts per space, keep this at 20 or lower. Higher values increase page load time and database query time proportionally.

Replies Per Page

Setting: replies_per_page Default: 20 Location: General tab → Pagination section

Controls how many replies load per page inside a single post view. This value also controls how many additional replies load each time a member clicks Load More in a thread. Pagination starts at the oldest replies and works forward. Members can jump to the last page to see the most recent replies.

Guest Access

Setting: guest_read Default: true (on) Location: General tab → Access section

When enabled, logged-out visitors can read all public spaces and posts without signing in. They see a prompt to log in when they try to vote, reply, or follow a space.

Turn this off if your community is members-only and you do not want any content visible to search engines or unregistered visitors.

Require Login to Participate

Setting: require_login Default: true (on) Location: General tab → Access section

When enabled, any action that writes data (posting, replying, voting, following) requires the user to be logged in. Guests are routed to Jetonomy's in-page sign-in form (no wp-login.php bounce) and returned to whatever they were doing once they sign in.

This is always recommended on. Disable it only if you are running a very specific open-participation setup where anonymous contributions make sense.

Note: Even with guest access enabled, anonymous posting is not supported. "Guest access" means read-only browsing for logged-out visitors.

Settings Save Confirmations

After you click Save Changes, a confirmation banner appears at the top of the settings page. The banner stays visible until you dismiss it - it does not disappear automatically. This ensures you always have a clear signal that your changes were saved.

What's Next?

Configure trust level thresholds and rate limits to control who can do what in your community.

Permission Settings →

The Permissions tab controls how quickly members earn trust in your community and how much they can do before you have had a chance to evaluate their behavior.

Permissions settings panel with trust level thresholds and rate limits

What You Will Learn

  • What Jetonomy's trust level system is and how it auto-promotes members
  • How to configure the thresholds for each trust level
  • What each trust level unlocks
  • How to adjust rate limits to match your community's size and risk tolerance

Go to Jetonomy → Settings → Permissions to access these settings.

The Trust Level System

Jetonomy uses six trust levels (0 through 5) to gradually extend posting privileges to members as they demonstrate good behavior. Levels 0 through 3 are earned automatically by the background cron job. Levels 4 and 5 are granted manually by moderators or admins.

Level Name Earned By
0 New Member Default on registration
1 Basic Automatic - light activity
2 Member Automatic - consistent participation
3 Regular Automatic - high engagement and reputation
4 Trusted Manual - granted by moderator or admin
5 Leader Manual - granted by admin only

The cron job runs every 12 hours and re-evaluates every member against the configured thresholds. Demotion is also possible - if a member is muted or receives too many spam flags, their trust level can drop.

Trust Level Thresholds

Setting: trust_thresholds Location: Permissions tab → Trust Levels section

Each auto-promotion level (1, 2, and 3) has a configurable set of thresholds. A member must meet all thresholds for a level to be promoted to it.

Threshold Description Configurable
min_posts Minimum posts created Yes
min_days Minimum days since registration Yes
min_visits Minimum session visits Yes
min_reputation Minimum reputation score Yes
max_flags Maximum accepted spam flags before block Yes

Default thresholds (Level 1 example):

Threshold Default Value
min_posts 1
min_days 1
min_visits 3
min_reputation 0

For small communities (under 200 members), lower the thresholds. Members can feel stuck if Level 1 requirements take weeks to meet. For larger communities, raise the thresholds to protect against spam waves.

Tip: Start with low thresholds and tighten them if you see abuse. It is easier to tighten later than to manually promote members who are stuck at Level 0.

What Each Trust Level Unlocks

Ability Level Required
Read public spaces 0 (any visitor)
Create posts 0
Reply to posts 0
Add images to posts 1
Include external links 1
Use @mentions 1
Follow spaces 0
Vote on posts and replies 0
Flag content for moderation 1
Edit own posts 0
Delete own posts 1
Access invite links 1
Create invite links 2
Skip anti-spam checks 2
Moderate content (space moderator) Assigned by admin

Note: The anti-spam exemption at Level 2 is important. Members who have earned Level 2 are trusted enough to skip reCAPTCHA and Turnstile checks entirely. This keeps the experience smooth for your most active members.

Rate Limits

Setting: rate_limits Location: Permissions tab → Rate Limits section

Rate limits cap how many actions a member can take in a given time window. They protect your community from spam bursts and accidental double-submissions.

Action Default Limit Window
Create post 3 per day
Create reply 10 per day
Vote 5 per day

Trust Level 1+ members are exempt from all rate limits.

For high-traffic communities, raise these limits. For communities experiencing spam problems, lower them. Changes take effect immediately - no cache flush needed.

Adjusting for Community Size

Small community (under 500 members):

  • Lower all trust thresholds significantly - active members should reach Level 2 within a week
  • Keep rate limits at defaults - spam volume is low
  • Consider setting min_days to 0 for Level 1 to avoid frustrating early adopters

Large community (10,000+ members):

  • Keep thresholds at defaults or raise them - organic promotion still happens, just slower
  • Raise max_flags threshold at Level 0 to prevent easy reputational attacks
  • Lower post rate limits if you see spam bursts

What's Next?

Configure which notification types are enabled by default and set your community email identity.

Email Settings →

The Email settings tab controls which notification emails Jetonomy sends, what name and address they come from, and how to test your email configuration.

Email settings with From name, From address, and notification type toggles

What You Will Learn

  • Which notification types can be toggled on or off
  • How to set your community's From name and From address
  • How to send a test email to confirm delivery
  • How to connect an SMTP plugin for reliable email delivery

Go to Jetonomy → Settings → Email to access these settings.

How Jetonomy Sends Email

Jetonomy uses WordPress's built-in wp_mail() function for all outgoing notifications. This means it is immediately compatible with any SMTP plugin you already use - WP Mail SMTP, FluentSMTP, Postman, or any other. No extra configuration in Jetonomy is needed; just configure your SMTP plugin and Jetonomy benefits automatically.

Tip: On production sites, always use an SMTP plugin with a transactional email service (Mailgun, Postmark, SendGrid, SES). The default PHP mail() delivery is unreliable and frequently lands in spam.

From Name

Setting: email_from_name Default: Your WordPress site name Location: Email tab → Sender section

This is the display name that appears in the From field of every Jetonomy notification email. Use your community or product name - something members will recognize immediately in their inbox.

From Address

Setting: email_from_email Default: WordPress admin email Location: Email tab → Sender section

This is the email address that appears in the From field. Use a dedicated address such as community@yoursite.com or noreply@yoursite.com.

Warning: The From address must be a verified sender with your email service provider. Using an unverified address causes high bounce rates and spam scoring. If you use Gmail SMTP, the From address must match your Google account.

Notification Toggles

Setting: notification_defaults Location: Email tab → Notification Types section

Each notification type has an independent toggle for both web (in-app bell) and email delivery. The defaults shown here are the site-wide defaults. Individual members can override their own preferences from their notification settings page.

Notification Type Web Default Email Default
Reply to your post On On
Reply to a reply you made On Off
@mention On On
Accepted answer (Q&A) On On
Vote on your post On Off
Badge earned On Off
New post in followed space On Off

Turning off a type at the site level disables it globally - individual members cannot re-enable a type you have disabled here. Use this to prevent email overload from noisy notification types.

Note: Vote and badge notifications default to web-only because they can occur frequently. Email for every vote would quickly train members to ignore your community emails entirely.

Test Email

Location: Email tab → bottom of page → Send Test Email button

Click Send Test Email to send a test message to the WordPress admin email address. The test email confirms that wp_mail() is working and that your From name and address are applying correctly.

If the test email does not arrive within a few minutes, check:

  1. Your SMTP plugin's log for send errors
  2. Your spam folder
  3. That the From address is verified with your email provider

Email and Jetonomy Pro

Jetonomy Pro adds two additional email capabilities:

  • Email Digest - daily and weekly summary emails that bundle multiple notifications into one. Members set their preference per notification type.
  • ESP Adapters - native integrations for SendGrid, Mailgun, Amazon SES, and Postmark that bypass wp_mail() for higher throughput and detailed delivery analytics.

Both are managed via Jetonomy → Extensions after installing Jetonomy Pro.

What's Next?

Control the visual appearance of your community - accent color, font inheritance, layout density, and custom CSS.

Appearance Settings →

The Appearance settings tab gives you direct control over the visual style of your community - from a single accent color override to a full custom CSS field.

Appearance settings with accent color picker, font options, and layout density controls

What You Will Learn

  • How to set a custom accent color
  • What the font and color inheritance toggles do
  • How layout density affects the community UI
  • How to use the custom CSS field safely

Go to Jetonomy → Settings → Appearance to access these settings.

How Jetonomy's Visual System Works

Jetonomy uses CSS custom properties (--jt-* tokens) throughout its stylesheet. Every color, font, radius, and spacing value references a token. By default, those tokens inherit from your active theme's theme.json values automatically.

The Appearance tab gives you a set of override controls on top of that inheritance layer. You can use them without writing any CSS.

Accent Color

Setting: accent_color Default: Inherited from theme (--wp--preset--color--primary) Location: Appearance tab → Colors section

The accent color drives buttons, links, vote arrows, trust-level highlights, and other interactive elements. Leave this blank to inherit from your theme. Set a specific hex value to override the theme's primary color just for Jetonomy.

This value is injected as --jt-accent on the .jt-app element at runtime, so it overrides the theme-inherited value.

Tip: Use a color that has at least a 4.5:1 contrast ratio against white (WCAG AA). The community UI places accent colors on white backgrounds frequently.

Inherit Fonts from Theme

Setting: inherit_fonts Default: On Location: Appearance tab → Typography section

When on, Jetonomy uses --jt-font: inherit - the community adopts whatever font your theme sets on the body element. This is the correct setting for most sites.

Turn this off only if you want Jetonomy to use a specific font independent of your theme. In that case, define --jt-font in the Custom CSS field.

Inherit Colors from Theme

Setting: inherit_colors Default: On Location: Appearance tab → Colors section

When on, the --jt-accent token pulls from --wp--preset--color--primary in your theme.json. This means the accent color stays in sync with theme updates automatically.

Turn this off if you have set a custom accent color above and do not want theme updates to override it.

Layout

Jetonomy 1.4.0 added a Layout panel with three controls that decide how the community canvas sits inside your active theme. Every option defaults to Theme Default, so existing installs see no visual change after the upgrade. When you do change a value, Jetonomy emits a small block of CSS scoped to body.jt-page - the rules only apply on community routes and never leak into the rest of your site.

Container Width

Setting: container_width Default: Theme Default Options: Theme Default, Full Width, Custom (px) Location: Appearance tab → Layout section

Controls how wide the community canvas can grow before it stops expanding.

  • Theme Default - Inherits the host theme's content container width. Use this when your theme's reading width already feels right.
  • Full Width - Lets the community stretch edge-to-edge of the viewport. Best for kanban-style spaces, leaderboards, and dense feeds that benefit from horizontal room.
  • Custom (px) - Pins the canvas to a specific pixel width (e.g. 1280). Useful when you want a wider reading column than the theme provides without going fully edge-to-edge.

Theme Sidebar

Setting: theme_sidebar Default: Theme Default Options: Theme Default, Hide on community pages Location: Appearance tab → Layout section

Decides whether the host theme's sidebar shows on community routes.

  • Theme Default - Leaves the theme's sidebar exactly where the theme renders it.
  • Hide on community pages - Suppresses the host theme's sidebar across /community/* so the forum renders at full width even when the rest of the site has a sidebar everywhere. Pair this with Full Width above when you want a true full-bleed community experience.

Page Padding

Setting: page_padding Default: Theme Default Options: Theme Default, None, Comfortable Location: Appearance tab → Layout section

Adjusts the inline padding around the community canvas.

  • Theme Default - Uses whatever inline padding the theme provides.
  • None - Removes the inline padding so the community sits flush against the viewport edges. Good for themes that already hug the edges.
  • Comfortable - Adds a generous inline padding. Useful for themes that hug the edges too tightly and leave content butting against the screen on mobile.

Tip: If your theme has a sidebar everywhere but you want the community to feel like a standalone app, set Container Width to Full Width, Theme Sidebar to Hide on community pages, and Page Padding to Comfortable. The rest of your site keeps the original theme layout.

Layout Density

Setting: layout_density Default: comfortable Options: Comfortable, Compact Location: Appearance tab → Layout section

Comfortable - Standard spacing between post cards, reply cards, and interface elements. Best for general communities and long-form discussion.

Compact - Reduced padding and tighter spacing. Fits more content on screen at once. Best for high-volume spaces where members scan many posts quickly.

When you change this setting, Jetonomy adds data-jt-density="compact" (or "comfortable") to the .jt-app wrapper element. CSS rules keyed to this attribute apply the appropriate spacing.

Custom CSS

Setting: custom_css Default: Empty Location: Appearance tab → Custom CSS section

The Custom CSS field accepts any valid CSS. Jetonomy outputs this CSS as an inline style block at the end of the <head> on all community pages, scoped after the main jetonomy.css stylesheet.

Use this field to override --jt-* tokens, adjust component styles, or add community-specific visual tweaks:

/* Override accent color and border radius */
.jt-app {
    --jt-accent: #7c3aed;
    --jt-radius: 12px;
}

/* Increase heading size in post titles */
.jt-post-title {
    font-size: 1.25rem;
}

Note: Custom CSS is output as-is - no minification, no scoping, no sandboxing. Write only CSS you control. If you enter invalid CSS here, it may break parts of the community UI.

Tip: For larger CSS customizations, consider using a child theme's style.css or a dedicated CSS plugin instead of this field. The Custom CSS field is best for quick, targeted overrides.

What's Next?

Configure how Jetonomy appears in search engines - XML sitemaps, schema markup, and meta title patterns.

SEO Settings →

The SEO settings tab controls how Jetonomy pages appear in search engines - XML sitemaps, structured data, meta title patterns, and robots directives for specific page types.

SEO settings with sitemap toggle, meta title patterns, and robots directives

What You Will Learn

  • How Jetonomy generates SEO-friendly URLs for community pages
  • How to enable or disable the XML sitemap
  • How schema markup works for forum content
  • How to configure meta title patterns
  • How to exclude certain page types from search engine indexing

Go to Jetonomy → Settings → SEO to access these settings.

How Jetonomy Generates URLs

Every Jetonomy page has a clean, human-readable URL structured around your community base slug:

Page Type URL Pattern
Community home /community/
Category /community/category/slug/
Space /community/s/slug/
Post /community/s/space-slug/t/post-slug/
User profile /community/u/username/
Tag /community/tag/slug/
Search /community/search/

Post slugs are auto-generated from the post title, truncated at 60 characters, and deduplicated with a numeric suffix if needed. All URLs use standard WordPress rewrite rules and are compatible with all SEO plugins.

XML Sitemap

Setting: seo_sitemap Default: On Location: SEO tab → Sitemap section

When enabled, Jetonomy registers a custom sitemap provider with WordPress's built-in sitemap API. Community pages appear at /wp-sitemap.xml alongside your regular WordPress content.

The sitemap includes:

  • All public spaces
  • All public posts (paginated if over 2,000)
  • User profile pages

Private, hidden, and archived spaces are excluded. Draft and scheduled posts are excluded.

Tip: Check Settings → Reading → Search engine visibility is not set to "Discourage search engines" or your sitemap will be disregarded.

Sitemap Link

Location: SEO tab → Sitemap section

Right next to the toggle is a button that surfaces the live sitemap URL for your install (typically https://example.com/wp-sitemap.xml). Copy the URL straight from the admin and submit it to:

You only need to submit the sitemap once per search engine. Google and Bing both recrawl the file automatically after the first submission, so new posts and spaces appear in the index without any further action.

Schema Markup

Setting: seo_schema Default: On Location: SEO tab → Structured Data section

When enabled, Jetonomy outputs JSON-LD structured data on community pages. This helps search engines understand your content and can improve how your pages appear in search results.

Page Type Schema Type
Community home WebSite + BreadcrumbList
Space DiscussionForumPosting (as container)
Single post DiscussionForumPosting
Post with replies DiscussionForumPosting + Comment
User profile Person

The DiscussionForumPosting type is the W3C-recognized schema for forum content. Google uses it to display rich results for Q&A content in particular.

Meta Title Patterns

Settings: seo_post_title, seo_space_title Location: SEO tab → Title Patterns section

These patterns control the <title> tag on Jetonomy pages. Use the available tokens to build the pattern you want.

Available tokens:

Token Output
{post_title} The post title
{space_name} The space name
{site_name} Your WordPress site name
{page_number} Current page number (reply pagination)

Default post title pattern: {post_title} - {space_name} | {site_name}

Default space title pattern: {space_name} | {site_name}

Tip: Keep titles under 60 characters to avoid truncation in search results. The {site_name} suffix is good for brand recognition but costs characters. For long space names, consider omitting {site_name}.

Noindex Controls

Settings: seo_noindex_profiles, seo_noindex_search Default: Off for profiles, On for search Location: SEO tab → Robots section

Noindex user profiles - When enabled, Jetonomy adds <meta name="robots" content="noindex"> to all /community/u/*/ pages. Enable this if you prefer profiles not to appear in search results (common for privacy-sensitive communities).

Noindex search results - When enabled, the /community/search/ page is excluded from indexing. This is on by default because search results pages provide minimal SEO value and can create duplicate-content signals.

Open Graph and Twitter Card Tags

Jetonomy outputs Open Graph and Twitter Card meta tags automatically on all public community pages. No setting is needed. These tags control how your posts appear when shared on social media:

  • og:title - the post or space title
  • og:description - the post excerpt (first 160 characters of body text)
  • og:type - article for posts, website for other pages
  • og:url - the canonical URL
  • twitter:card - summary (or summary_large_image if a post has an image attachment)

Note: If you use an SEO plugin like Yoast SEO, RankMath, or The SEO Framework, its OG tags may override Jetonomy's. This is fine - SEO plugin output takes priority via standard WordPress wp_head hook priority ordering.

Twitter / X Handle

Setting: seo_twitter_handle Default: Empty Location: SEO tab → Twitter handle

Enter your community's Twitter or X handle (with or without the leading @). When set, Jetonomy emits twitter:site and twitter:creator meta tags on every community page that does not already declare a per-page override. This gives X / Twitter a verified attribution to display alongside link previews and unlocks richer card layouts.

Leave this empty if the community has no Twitter / X presence - Jetonomy simply omits the tags instead of emitting empty ones.

Default Share Image

Setting: seo_default_share_image Default: Empty Location: SEO tab → Default share image

Pick an image from the WordPress media library to use as the fallback og:image whenever a specific post does not have an image of its own. Posts that already include their own image continue to use that image - this setting only kicks in when there is nothing else to show.

Recommended specs:

Property Value
Dimensions 1200 x 630 px
Format PNG or JPG
File size Under 5 MB
Aspect ratio 1.91:1

Twitter / X, Facebook, LinkedIn, and Slack all read this image when generating link previews, so picking a branded fallback (logo + community name on a clean background) keeps shared links recognizable.

What's Next?

Set up anti-spam protection to keep your community clean without frustrating legitimate members.

Anti-Spam Settings →

The Anti-Spam tab lets you add invisible bot protection to post and reply submission - without disrupting the experience for legitimate members.

Anti-spam settings with provider selection, API keys, and score threshold

What You Will Learn

  • Which anti-spam providers Jetonomy supports
  • How to add your API keys and enable protection
  • How the score threshold works for reCAPTCHA v3
  • Which members are automatically exempt from checks

Go to Jetonomy → Settings → Anti-Spam to access these settings.

How It Works

Anti-spam checks run server-side before a post or reply is saved. When a submission fails the check, Jetonomy blocks the save and returns an error to the user. The check is invisible to legitimate members - no checkbox to tick, no image to identify.

Members at Trust Level 2 or above are exempt from all anti-spam checks. Admins are always exempt. This ensures your most active, trusted members never encounter friction.

Choosing a Provider

Setting: antispam_provider Default: None Options: None, Google reCAPTCHA v3, Cloudflare Turnstile

Provider How It Works User Visibility
None No spam protection -
Google reCAPTCHA v3 Score-based, no user interaction Invisible (small badge)
Cloudflare Turnstile Smart challenge, no image puzzle Invisible (small badge)

Google reCAPTCHA v3 assigns a risk score (0.0 to 1.0) to each submission. You set a threshold - submissions below it are blocked.

Cloudflare Turnstile is GDPR-friendlier than reCAPTCHA and does not show a challenge unless it detects suspicious behavior. It is a good default for EU communities.

Tip: If you are seeing bot spam despite having anti-spam enabled, also check your Trust Level 0 rate limits in the Permissions tab. Combining low rate limits with Turnstile stops most automated spam without any user friction.

Google reCAPTCHA v3

  1. Go to google.com/recaptcha/admin and create a new site.
  2. Select reCAPTCHA v3 as the type.
  3. Add your domain to the allowed domains list.
  4. Copy the Site Key and Secret Key.
  5. Paste them into the corresponding fields in Jetonomy → Settings → Anti-Spam.
  6. Set the Score Threshold (see below).
  7. Save settings.

Score Threshold (reCAPTCHA v3 Only)

Setting: recaptcha_score_threshold Default: 0.5 Range: 0.1 to 0.9

Google's reCAPTCHA v3 returns a score between 0.0 (likely bot) and 1.0 (likely human). Jetonomy blocks any submission with a score below your threshold.

Threshold Effect
0.3 Block only very confident bots. May let some spam through.
0.5 Balanced default. Blocks most bots, rarely affects humans.
0.7 Strict. May occasionally block mobile users or VPN users.

Start at 0.5. If you see false positives (legitimate members getting blocked), lower to 0.4. If spam is still getting through, raise to 0.6.

Cloudflare Turnstile

  1. Go to dash.cloudflare.comTurnstile and add a new site.
  2. Select Invisible as the widget mode.
  3. Add your domain to the allowed hostnames list.
  4. Copy the Site Key and Secret Key.
  5. Paste them into Jetonomy → Settings → Anti-Spam.
  6. Save settings.

There is no score threshold for Turnstile - Cloudflare handles the risk scoring internally. A submission either passes or fails.

Note: Turnstile requires your domain to be exactly correct in the Cloudflare dashboard. www.yoursite.com and yoursite.com are treated as different hostnames.

Trust Level Exemptions

Members at Trust Level 2 or above are never shown a challenge and their submissions never go through the anti-spam check. The check is skipped entirely on the server side.

This is intentional. Trust Level 2 requires consistent participation over time - members who earn it are clearly not bots.

WordPress admins (manage_options capability) are also always exempt, regardless of trust level.

Testing Your Setup

  1. Log out of WordPress completely.
  2. Go to any space on your community.
  3. Try to create a post or reply.
  4. If protection is working, the submission should complete (legitimate human request) and no error should appear.

To test blocking, lower the reCAPTCHA threshold to 0.9 temporarily and try submitting. If the error message appears, your integration is working correctly. Reset to 0.5 afterward.

What's Next?

Migrate your existing community data from bbPress or wpForo into Jetonomy.

Importing from bbPress →

The Access Control setting decides whether your community is open to the public or hidden behind sign-in. Pick the right mode in one place and Jetonomy enforces it across every page and the REST API.

What You Will Learn

  • The difference between Public and Private community modes
  • Which pages stay reachable in Private mode (sign-in, register, lost password)
  • How REST API access changes between the two modes
  • When to switch and what to expect

Go to Jetonomy → Settings → General → Access Control to choose the mode.

Public Mode (default)

Anyone - including search engines and visitors who haven't signed in - can read posts, replies, and member profiles. Posting and voting still require sign-in.

This is the default for every community and is unchanged from prior versions. Existing communities continue working without any setting change after upgrading to 1.4.1.

Use Public mode when:

  • You want search engine traffic to find your community
  • You're running a customer support forum or open knowledge base
  • New visitors should be able to browse before signing up

Private Mode

Every community page requires sign-in. Guests visiting /community/ or any space, post, tag, or profile URL are redirected to the sign-in page. The REST API also rejects unauthenticated requests for community data.

The sign-in, register, and forgot-password pages stay reachable so guests can still create an account or recover access.

Use Private mode when:

  • The community is for paying members only
  • Discussions are confidential (internal team, private group, paid coaching)
  • You don't want search engines to index any community content

What Stays Public in Private Mode

These pages are intentionally exempt so guests can sign up and recover access:

  • The sign-in page
  • The registration page
  • The forgot-password page
  • Email verification and password-reset confirmation links

Everything else - homepage, spaces, posts, replies, tags, member profiles, leaderboard, search - is gated.

REST API Behaviour

Public mode: read endpoints return data to anyone. Write endpoints still require auth.

Private mode: every endpoint under /wp-json/jetonomy/v1/ requires an authenticated request. Anonymous calls return 401 Unauthorized. This is checked centrally - third-party clients calling the API see the same gate as the website does.

Switching Modes

Switching is instant and applies to the next page load. You can change the mode at any time:

  • Public → Private: existing public links become sign-in redirects. Search engines will eventually drop your indexed pages.
  • Private → Public: pages become reachable again. Submit your sitemap to search engines if you want re-indexing.

There's no migration step and no downtime.

The Activity Log admin page shows you every audit-worthy event in your community - who created a post, who approved a reply, who banned a member, when a role changed. Read-only and filterable.

What You Will Learn

  • What events are recorded in the Activity Log
  • How to filter the log by user, event type, or date range
  • Why some events appear and others don't
  • How to use the log for moderation reviews and account audits

Go to Jetonomy → Activity Log to access the page.

What Gets Logged

The log records every event that changes the state of the community in a way that's worth being able to look back on:

  • Posts created, edited, deleted, restored
  • Replies created, edited, deleted, restored
  • Flags raised and resolved (with the resolution outcome)
  • Members joining or leaving a space
  • Role changes (member → moderator, moderator → admin, demotions)
  • Trust level promotions and demotions
  • Bans and silences (issued and lifted)
  • Setting changes that affect community behaviour

What's NOT logged: votes, reads, search queries, page views - these are too high-volume to keep useful and are tracked separately by Analytics (Pro).

Filters

The page supports three independent filters that can be combined:

Filter What it does
User Show only events caused by a specific member or staff user
Type Show only one event type (e.g. "post created", "ban issued")
Date range Show events between two dates

Combine all three to answer questions like "which posts did this moderator approve last week?"

Read-Only

The Activity Log is intentionally read-only. You can't edit or delete entries from this page. The log is the audit trail - modifying it would defeat the point.

If you need to undo something a member or staff user did, do it in the relevant area (e.g. restore a deleted post from the post page, lift a ban from the member's profile). The log will then record the corrective action as a new entry.

Performance

The log is paginated server-side at 50 entries per page. Filters apply at the database level, so even on a community with hundreds of thousands of entries, filtered queries return in under a second on a normal host.

There's no automatic cleanup - entries are kept indefinitely. If you want to prune the log, you can do it via WP-CLI; reach out via support if you need a recipe.

The Revisions admin page lets you browse every saved revision of every post and reply, and compare any two of them side-by-side. Use it to see what changed when a member edits, or to recover content from an earlier version.

What You Will Learn

  • When Jetonomy saves a revision (and when it doesn't)
  • How to find a specific revision by post, user, or date
  • How to read the side-by-side diff
  • The relationship between the Revisions page and the post-level edit history

Go to Jetonomy → Revisions to access the page.

When a Revision Is Saved

A revision is saved every time a post or reply is edited and the change is meaningful - that is, the content actually differs from the previous version. Pure formatting tweaks (e.g. adding a paragraph break that doesn't change the rendered output) won't always create a revision.

Revisions are NOT created when:

  • The post is first published - that's the initial version, not a revision
  • The edit is from the same user within the auto-save window (a few seconds)
  • Only metadata changes (e.g. tags or pinned-state) - those go into the Activity Log, not Revisions

Browsing Revisions

The list view shows every revision sorted by date. Use the filters to narrow:

Filter What it does
Post Show every revision of one specific post or reply
User Show every revision authored by one user (across posts)
Date range Show revisions saved between two dates

Each row shows the post title, the editor, when the change was saved, and the size delta (e.g. "+128 chars / −42 chars").

Side-by-Side Diff

Click any revision to open the diff view. Pick two revisions from the same post and you'll see:

  • The before version on the left, after on the right
  • Added text highlighted green, removed text highlighted red
  • Line numbers so you can locate changes in context

The diff is character-aware, so small typo fixes show as small highlights rather than entire-paragraph rewrites.

Read-Only

Like the Activity Log, the Revisions page is read-only. To roll back to a prior revision, edit the post and paste the older content in. There's no one-click revert button - that's a deliberate choice to keep the audit trail truthful.

Permission

Only users with the jetonomy_manage_revisions capability (admin and moderator roles by default) can see this page. Members can see their own edit history on each individual post, but not the cross-post Revisions admin view.

Storage

Revisions are stored in a dedicated database table separate from WordPress core revisions. They don't bloat wp_posts, and they're not affected by the WP_POST_REVISIONS constant. Jetonomy keeps every revision indefinitely; if you need a retention policy, reach out via support.

Migration

Import from bbPress, wpForo, and other forum plugins.

Move your existing bbPress community into Jetonomy - forums, topics, replies, user data, and vote history - using the built-in importer.

Import tool interface with source selection and progress tracking

What You Will Learn

  • What data the bbPress importer brings over and what it leaves behind
  • How to prepare your site before running the import
  • How to start the import, monitor progress, and resume if it stops
  • How to use the dry-run option to estimate time and check for issues
  • What to verify after the import completes

What Gets Imported

bbPress Data Imported As Notes
Forums Jetonomy Spaces Forum description → space description
Topics Jetonomy Posts Topic title + content preserved
Replies Jetonomy Replies Threaded up to 3 levels; deeper threads flattened
Tags Jetonomy Tags Applied to posts
User accounts Linked to existing WP users Matched by user ID
User activity counts Reputation score Approximate mapping
Votes (if present) Jetonomy votes Only if bbPress vote plugin data is in standard tables
Forum moderators Space Moderator role Matched to WP users by ID
Sticky topics Pinned posts Preserved

Not imported:

  • bbPress subscriptions (replaced by Jetonomy follow/subscribe)
  • bbPress private messages (import to Jetonomy Pro private messaging separately)
  • Custom bbPress meta fields (use the jetonomy_importers filter to extend)
  • Forum avatars (WordPress avatars carry over via Gravatar/WP user accounts)

Pre-Import Checklist

Complete these steps before starting the import:

  1. Back up your database. The importer does not modify bbPress tables, but a backup is essential.
  2. Activate Jetonomy and complete the setup wizard. Your community base URL should be set.
  3. Keep bbPress active during the import. The importer reads directly from bbPress tables.
  4. Set your server timeout high. Large imports (100,000+ records) take time. Increase max_execution_time in php.ini or use WP-CLI (recommended for large sites).
  5. Disable other heavy plugins during import if your server is resource-constrained.

Tip: For large communities (10,000+ topics), run the import via WP-CLI to avoid browser timeouts entirely. See the WP-CLI section below.

Running the Import

  1. Go to Jetonomy → Import in your WordPress admin.
  2. Select bbPress as the source.
  3. (Optional) Enable Dry Run to preview results without writing any data.
  4. Click Start Import.

The importer processes records in batches of 50. A progress bar shows completion percentage, current batch, and estimated time remaining.

Do not close the browser tab while the import is running. If the page refreshes or you navigate away, the import will pause - but can be resumed (see below).

Dry-Run Mode

Enable Dry Run before your first real import. In dry-run mode, Jetonomy reads all your bbPress data, validates it, and reports:

  • Total record counts (forums, topics, replies, users)
  • Estimated import time
  • Any data integrity issues (orphaned topics, missing user accounts, encoding problems)
  • A preview of the first 10 forum-to-space mappings

Dry-run mode makes no database writes. Run it as many times as you need.

Estimated Import Times

Community Size Topics + Replies Estimated Time
Small Under 10,000 2-5 minutes
Medium 10,000-100,000 10-40 minutes
Large 100,000-500,000 1-4 hours
Very large 500,000+ Use WP-CLI

These are estimates for a typical shared hosting server. Dedicated servers will be significantly faster.

Resuming a Paused Import

If the import stops (browser closed, timeout, server restart), return to Jetonomy → Import. The importer stores its progress in the database. Click Resume Import to continue from the last completed batch.

You can safely resume multiple times. Records that were already imported are skipped.

Running via WP-CLI

For large communities, WP-CLI is more reliable than the browser-based importer:

wp --path="/path/to/wordpress" jetonomy import run --source=bbpress

Optional flags:

# Dry run
wp jetonomy import run --source=bbpress --dry-run

# Set batch size (default 50)
wp jetonomy import run --source=bbpress --batch-size=100

# Resume from a specific batch offset
wp jetonomy import run --source=bbpress --offset=500

WP-CLI runs without a timeout limit and outputs a progress line for each batch.

Post-Import Checklist

After the import completes, verify the following:

  • Navigate to your community home - spaces should match your old bbPress forums
  • Open several posts and confirm content and replies are intact
  • Check that user profiles show post counts
  • Verify that moderators have the Moderator role in their respective spaces
  • Test creating a new post as a regular user
  • Visit Jetonomy → Settings → Permalinks and click Save to flush rewrite rules
  • If you used bbPress shortcodes on pages, remove or replace them - they will output raw shortcode text now that bbPress is still active

Note: After a successful import, you can deactivate bbPress. Your community data is now in Jetonomy's tables and bbPress is no longer needed.

What's Next?

Migrating from wpForo? The process is similar with a few wpForo-specific field mappings.

Importing from wpForo →

Move your existing wpForo community into Jetonomy - forums, topics, replies, user profiles, and reputation data - using the built-in wpForo importer.

Import tool with wpForo source selected and migration progress

What You Will Learn

  • What the wpForo importer brings over and how wpForo structures map to Jetonomy
  • How to prepare your site before running the import
  • How to start, monitor, and resume the import
  • How wpForo-specific fields (reputation, badges, user roles) are handled
  • What to verify after the import

What Gets Imported

wpForo Data Imported As Notes
Forums Jetonomy Spaces Forum name, slug, description preserved
Sub-forums Jetonomy Sub-spaces Nested up to 2 levels
Topics Jetonomy Posts Title and first post content preserved
Replies (posts) Jetonomy Replies Threaded up to 3 levels; deeper threads flattened
Tags Jetonomy Tags Applied to corresponding posts
User accounts Linked to existing WP users Matched by WP user ID
User reputation Reputation score wpForo points mapped to Jetonomy score (1:1)
User roles (Moderator) Space Moderator role Per-forum moderator assignments preserved
Liked posts Vote score wpForo likes mapped to upvotes
Pinned topics Pinned posts Preserved
Closed topics Closed posts Preserved

Not imported:

  • wpForo private conversations (different data structure; import to Jetonomy Pro private messaging separately)
  • wpForo groups and group memberships (map manually to Jetonomy spaces)
  • wpForo custom user groups beyond standard roles
  • Embedded wpForo media that uses shortcodes instead of standard <img> tags

wpForo Data Structure Differences

wpForo and Jetonomy structure their data differently in a few key areas:

Multi-board support - wpForo supports multiple boards, each with its own set of forums, topics, and posts stored in separate database tables (e.g., wp_wpforo1_forums for board 1, wp_wpforo2_forums for board 2). The importer automatically detects all active boards and imports each into its own Jetonomy category. Single-board installs work without any extra configuration.

Forums and categories - Within each board, wpForo uses a hierarchical forums table. Jetonomy separates categories (top-level groups) from spaces (discussion areas). The importer creates a Jetonomy category per board and spaces from the forums within each board.

Post structure - In wpForo, the first "post" of a topic is a reply in the same table. In Jetonomy, topics and replies are separate entities. The importer promotes the first wpForo post as the Jetonomy post body and imports subsequent posts as replies.

Reputation - wpForo tracks reputation as a single integer. Jetonomy maps it directly as the starting reputation score. Trust levels are re-evaluated by the cron job after import based on your configured thresholds.

Pre-Import Checklist

  1. Back up your database. The importer reads but never modifies wpForo tables, but a backup protects against any edge cases.
  2. Activate Jetonomy and complete the setup wizard.
  3. Keep wpForo active during the import - the importer reads from wpForo's live tables.
  4. Check your wpForo table prefix. If wpForo uses a custom prefix, confirm it in wpforo_boards - the importer auto-detects it.
  5. Disable wpForo page caching if active, to avoid stale data during the import process.

Tip: If you have over 50,000 topics, use WP-CLI to run the import. Browser-based imports on large databases can time out on shared hosting.

Running the Import

  1. Go to Jetonomy → Import in your WordPress admin.
  2. Select wpForo as the source.
  3. (Optional) Enable Dry Run to preview the import before writing any data.
  4. Click Start Import.

The importer processes in batches of 50 records. A progress indicator shows total records, completed batches, and estimated time remaining.

Dry-Run Mode

Run a dry run before your first import. It analyzes your wpForo database and reports:

  • Board hierarchy and how it maps to Jetonomy categories and spaces
  • Total topic, reply, and user counts
  • Estimated import duration
  • Any encoding issues or records with missing user references
  • A preview of the first 10 forum-to-space mappings

No data is written during a dry run. Run it as many times as needed.

Estimated Import Times

Community Size Topics + Replies Estimated Time
Small Under 10,000 2-5 minutes
Medium 10,000-100,000 10-40 minutes
Large 100,000-500,000 1-4 hours
Very large 500,000+ Use WP-CLI

Resuming a Paused Import

If the import pauses (due to a timeout, server restart, or closed browser), return to Jetonomy → Import and click Resume Import. Progress is stored in the database. Already-imported records are skipped automatically.

Running via WP-CLI

wp --path="/path/to/wordpress" jetonomy import run --source=wpforo

Optional flags:

# Dry run
wp jetonomy import run --source=wpforo --dry-run

# Set batch size (default 50; larger batches are faster on good servers)
wp jetonomy import run --source=wpforo --batch-size=100

# Resume from a specific batch offset
wp jetonomy import run --source=wpforo --offset=200

Handling wpForo's Custom Post Formats

wpForo supports "post types" (Normal, Question/Answer, Debate). The importer maps them to Jetonomy space types:

wpForo Board Type Jetonomy Space Type
Standard Forum
Q&A Q&A
Debate Forum (closest match)

If you had a mix of types in a single wpForo board, the importer uses the board's configured type as the Jetonomy space type.

Post-Import Checklist

After the import completes:

  • Visit your community home and confirm spaces match your old wpForo boards
  • Open several posts from different spaces and verify content is intact
  • Check that Q&A spaces show accepted answers correctly
  • Verify user reputation scores on a few known high-reputation members
  • Confirm that forum moderators have the Moderator role in their spaces
  • Go to Settings → Permalinks and click Save to flush rewrite rules
  • Remove or update any wpForo shortcodes on pages and widgets
  • Consider deactivating wpForo after confirming the import - it is no longer needed

Note: If your wpForo installation used a third-party plugin for member ratings or post reactions, those values are not included in the standard import. You can extend the importer using the jetonomy_importers filter.

What's Next?

Now that your community is live, configure who can read it and how members earn trust.

General Settings →

Why Jetonomy

How Jetonomy compares to bbPress, wpForo, and other solutions.

How Jetonomy compares to bbPress - and why communities are switching to a modern forum experience.

Jetonomy community home page with modern UI and space listings

What You Will Learn

  • Key differences between Jetonomy and bbPress
  • Where Jetonomy excels for growing communities
  • What bbPress still does well

Try a live Jetonomy community before you commit - Wbcom runs its own public support community on Jetonomy at community.wbcomdesigns.com. Browse spaces, read real support threads, and get a feel for the voting, trust-level badges, reply threading, and moderation flow on a production site.

Feature Comparison

Feature bbPress Jetonomy
Data storage WordPress custom post types Custom database tables (24 tables)
Threaded replies 1 level 3 levels
Voting Not built-in (requires add-on) Built-in upvote/downvote
Q&A with accepted answers Not available Built-in (per-space type)
Idea boards Not available Built-in with status workflow
Trust levels Not available 6-level auto-promotion system
Full-text search WordPress default search FULLTEXT index with filters
Real-time interactions Page reload required WordPress Interactivity API
Moderation queue Basic Flag system + queue + auto-rules (Pro)
Anti-spam Akismet only Akismet + reCAPTCHA + Turnstile
REST API Limited 48+ endpoints (90+ with Pro)
Private messaging Not built-in Built-in (Pro)
Polls Not built-in Built-in (Pro)
Analytics Not available Dashboard with export (Pro)
Migration N/A Built-in bbPress importer

Where Jetonomy Excels

Performance at Scale

bbPress stores every topic and reply as a WordPress post. On sites with 10,000+ topics, this bloats the wp_posts and wp_postmeta tables, slowing down your entire WordPress installation - not just the forum.

Jetonomy uses its own database tables with proper indexes and cursor-based pagination. Your forum can grow to tens of thousands of topics without affecting the rest of your site.

Self-Moderating Community

bbPress relies entirely on WordPress roles. You either trust someone to moderate or you do not. There is no middle ground.

Jetonomy's trust level system automatically promotes members as they contribute. New users start restricted. Active, helpful members gradually earn the ability to edit, moderate, and manage - without any manual role changes.

Modern User Experience

bbPress was designed before mobile-first became standard. Jetonomy is built with responsive CSS custom properties that adapt to any theme, inline editing, instant voting, and real-time notifications - all without page reloads.

Where bbPress Still Works

bbPress is a good choice if you need a simple, lightweight forum with minimal features and your community will stay small (under 1,000 topics). It has been around since 2011 and has a large ecosystem of third-party add-ons.

If you are already running bbPress and considering a switch, Jetonomy includes a built-in bbPress importer that migrates all your forums, topics, replies, and user data.

What's Next?

How Jetonomy compares to wpForo - two modern forum plugins with different approaches.

Jetonomy community home page showcasing the modern forum interface

What You Will Learn

  • Key architectural differences between Jetonomy and wpForo
  • Feature comparison across both plugins
  • Which plugin fits your use case

Feature Comparison

Feature wpForo Jetonomy
Data storage Custom tables Custom tables
Forum layouts 4 built-in layouts 4 space types (forum, Q&A, ideas, feed)
Threaded replies Flat + nested option 3-level threading
Voting Likes only Upvote/downvote with reputation
Trust system Manual user groups Auto-promoting trust levels (0-5)
Q&A mode Basic question/answer Full Q&A with accepted answers
Idea boards Not built-in Built-in with status workflow
Real-time UI Page reload WordPress Interactivity API
Search Built-in search FULLTEXT with advanced filters
Anti-spam reCAPTCHA v2 reCAPTCHA v3 + Turnstile (invisible)
Topic management Move topics Move + merge + split
Draft posts Not available Save as draft + scheduling
REST API Limited 48+ endpoints (90+ with Pro)
Theme integration Custom styling CSS custom properties via theme.json
Membership gating Built-in groups Adapter system (MemberPress, PMPro, WooCommerce, LearnDash)
Analytics Basic stats Full dashboard with export (Pro)
Migration N/A Built-in wpForo importer

Where Jetonomy Stands Out

WordPress-Native Architecture

wpForo was originally built as a standalone forum that happens to run inside WordPress. Jetonomy was built WordPress-first - it uses the Interactivity API, theme.json design tokens, wp_cache, WP-Cron, and the WordPress REST API as its foundation.

This means Jetonomy integrates more deeply with WordPress features like block themes, site editing, and the Abilities API.

Trust Over Roles

wpForo uses manual user groups (similar to WordPress roles). You create groups, assign permissions, and manually move users between groups.

Jetonomy automates this entirely. New members start restricted and earn trust through participation. The community moderates itself as members advance through trust levels. You configure the thresholds once and the system handles promotions automatically.

Invisible Anti-Spam

wpForo supports reCAPTCHA v2 - the "I'm not a robot" checkbox that interrupts every user. Jetonomy uses reCAPTCHA v3 and Cloudflare Turnstile, both invisible. Members never see a CAPTCHA, and trusted members (Trust Level 2+) are completely exempt.

Adapter-Based Integrations

wpForo has built-in membership gating but requires wpForo-specific extensions for each membership plugin. Jetonomy uses a universal adapter pattern - MemberPress and PMPro work out of the box in free, and Pro adds WooCommerce, LearnDash, and Restrict Content Pro. Custom adapters can be built for any membership system.

Where wpForo Works Well

wpForo is a mature product with a large installed base. It offers built-in user groups, a forum-specific SEO system, and multiple layout options. If you need a traditional forum with minimal configuration, wpForo is a solid choice.

If you are running wpForo and want to migrate, Jetonomy includes a built-in wpForo importer.

What's Next?

How Jetonomy is built to handle communities of any size - from 10 members to 100,000+.

Space page with sidebar showing topic listings and member activity

What You Will Learn

  • Why custom database tables matter for performance
  • How cursor-based pagination prevents slowdowns
  • Caching strategies that keep page loads fast
  • Real-world performance benchmarks

The Problem with CPT-Based Forums

Most WordPress forum plugins (including bbPress) store every topic and reply as a row in wp_posts, with metadata in wp_postmeta. This approach works for small forums but creates serious problems as you grow:

  • Table bloat: 10,000 topics with 50,000 replies means 60,000 extra rows in wp_posts - slowing down every WordPress query, not just forum queries.
  • Meta queries: Fetching vote counts, view counts, or sticky status requires JOIN operations against wp_postmeta - one of the slowest query patterns in WordPress.
  • No proper indexes: wp_posts was designed for blog posts, not forums. It lacks indexes for common forum queries like "sort by vote score" or "filter by space."

How Jetonomy Solves This

Custom Database Tables

Jetonomy stores all community data in 24 dedicated tables with the wp_jt_ prefix. Each table has purpose-built columns and indexes:

  • Posts table: vote_score, reply_count, view_count, last_reply_at are real columns - not meta. Sorting by popularity is a simple ORDER BY vote_score DESC with an index hit.
  • Replies table: Indexed by post_id and parent_id for fast threaded reply loading.
  • Votes table: Composite key on (user_id, object_type, object_id) - checking "did this user vote?" is a single index lookup.

Your WordPress wp_posts table stays clean. Your forum can grow without slowing down the rest of your site.

Cursor-Based Pagination

Traditional pagination uses LIMIT/OFFSET. On page 500 of a 10,000-topic space, the database must scan and skip 9,980 rows before returning 20. This gets slower as your community grows.

Jetonomy uses cursor-based pagination: "give me 20 topics after ID 9980." The database uses the primary key index to jump directly to the right position. Page 500 loads as fast as page 1.

Smart Reply Loading

A topic with 400 replies does not load all 400 at once. Jetonomy loads the first 10 and last 10 replies, with a "load more" gap in between. Members see the opening conversation and the latest activity immediately.

When they click the gap, only the missing replies are fetched via the REST API - no full page reload.

Built-In Caching

Jetonomy uses WordPress object cache (wp_cache) throughout:

  • Permission checks: Cached for 60 seconds per user per space. A page with 30 replies does not run 30 permission queries.
  • Online status: Cached for 60 seconds. Showing green dots on 30 reply avatars requires 0 extra database queries.
  • Last seen tracking: Rate-limited to 1 database write per user per minute, not per page view.

If you run an object cache plugin (Redis, Memcached), Jetonomy benefits automatically.

Denormalized Counters

Jetonomy does not run COUNT(*) queries to show "42 replies" on a topic card. The reply_count column is updated incrementally when replies are created or deleted. Displaying a list of 20 topics with accurate counts requires zero extra queries.

Real-World Performance

Community Size Page Load (no cache) Page Load (Redis)
100 topics, 500 replies ~120ms ~80ms
1,000 topics, 5,000 replies ~180ms ~100ms
10,000 topics, 50,000 replies ~350ms ~150ms
50,000 topics, 200,000 replies ~500ms ~200ms

These are topic listing page loads (20 topics per page) on a standard VPS (2 CPU, 4GB RAM, SSD). Single topic pages with 30 replies load in similar times.

Tip: For the best performance on communities with 5,000+ members, enable an object cache plugin like WP Redis or W3 Total Cache with Memcached.

What You Can Do

For Small Communities (under 1,000 members)

No special configuration needed. Jetonomy works well on shared hosting with default settings.

For Medium Communities (1,000-10,000 members)

  • Enable object caching (Redis or Memcached)
  • Set posts per page to 20-30 (default)
  • Use a CDN for static assets

For Large Communities (10,000+ members)

  • Object caching required
  • Consider a VPS or managed WordPress host
  • Monitor with Query Monitor plugin
  • Review trust level thresholds - fewer moderators means fewer permission lookups

What's Next?

Developer Guide

REST API, hooks, template overrides, and extending Jetonomy.

Jetonomy exposes a full REST API under the jetonomy/v1 namespace: 48+ endpoints in the free plugin, plus 40+ additional endpoints when Jetonomy Pro is active (90+ total). All endpoints return JSON and integrate with WordPress nonce authentication via the wp_rest nonce.

Base URL: https://example.com/wp-json/jetonomy/v1/

Authentication

Public endpoints (marked Public below) return data without authentication. Write operations and moderation endpoints require a logged-in user and the X-WP-Nonce header:

const nonce = window.wpApiSettings?.nonce
           ?? jetonomyState?.nonce;  // Injected via wp_interactivity_state()

fetch( '/wp-json/jetonomy/v1/spaces/1/posts', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': nonce,
    },
    body: JSON.stringify({ title: 'My topic', content: '<p>Hello</p>' }),
} );

The Interactivity API store exposes apiBase and _nonce in the jetonomy namespace so the bundled frontend needs no extra configuration.


Categories

Manage the top-level taxonomy that groups Spaces.

Method Route Auth Description
GET /categories Public List all categories
POST /categories manage_options Create a category
GET /categories/{id} Public Get a single category
PATCH /categories/{id} manage_options Update a category
DELETE /categories/{id} manage_options Delete a category

GET /categories - example

const res  = await fetch( '/wp-json/jetonomy/v1/categories' );
const data = await res.json();
// data.data → array of category objects
// { id, name, slug, description, position, space_count }

Spaces

Spaces are the primary containers for posts (equivalent to forums or boards).

Method Route Auth Description
GET /spaces Public List spaces (paginated)
POST /spaces manage_options Create a space
GET /spaces/{id} Public Get a single space
PATCH /spaces/{id} Moderator / Admin Update space settings
DELETE /spaces/{id} manage_options Delete a space
GET /spaces/{id}/members Public / Members only if private List space members
POST /spaces/{id}/members Logged in Join a space
PATCH /spaces/{id}/members/{user_id} Moderator / Admin Change a member's role
DELETE /spaces/{id}/members/{user_id} Moderator / Admin Remove a member
POST /spaces/{id}/invite Moderator / Admin Generate an invite link
GET /invite/{token} Public Resolve an invite token

GET /spaces - parameters

Parameter Type Default Description
page int 1 Page number
per_page int 20 Results per page (max 100)
category_id int - Filter by category
search string - Search by title
orderby string position position, title, member_count, post_count
const res  = await fetch( '/wp-json/jetonomy/v1/spaces?per_page=10&category_id=3' );
const data = await res.json();
// data.data → array of space objects
// data.meta → { total, pages, page }

Posts

Posts are individual discussion threads (topics) inside a Space.

Method Route Auth Description
GET /spaces/{space_id}/posts Public List posts in a space
POST /spaces/{space_id}/posts Logged in Create a post
GET /posts/{id} Public Get a single post
PATCH /posts/{id} Author / Moderator Update a post
DELETE /posts/{id} Author / Moderator Delete a post
POST /posts/{id}/close Moderator / Admin Toggle closed status
POST /posts/{id}/pin Moderator / Admin Toggle pinned status
POST /posts/{id}/move Moderator / Admin Move to another space
POST /posts/{id}/merge Moderator / Admin Merge into another post
GET /posts/drafts Logged in List current user's drafts
GET /link-preview Public Fetch OG metadata for a URL

GET /spaces/{space_id}/posts - parameters

Parameter Type Default Description
page int 1 Page number
per_page int 20 Max 100
sort string latest latest, oldest, votes, replies
type string - Filter by post type (discussion, question, idea)
tag string - Filter by tag slug
status string publish publish, draft (author/mod only)

POST /spaces/{space_id}/posts - body

await fetch( `/wp-json/jetonomy/v1/spaces/${spaceId}/posts`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
    body: JSON.stringify({
        title:   'How do I configure caching?',
        content: '<p>Looking for recommendations...</p>',
        type:    'question',     // discussion | question | idea
        tags:    ['caching', 'performance'],
        status:  'publish',      // or 'draft'
    }),
} );

POST /posts/{id}/move - body

{ target_space_id: 42 }

POST /posts/{id}/merge - body

{ target_post_id: 17 }

GET /link-preview - parameters

Parameter Type Required Description
url string Yes URL to fetch OG data for

Response: { title, description, image, domain }


Replies

Replies are threaded responses to a Post.

Method Route Auth Description
GET /posts/{post_id}/replies Public List replies on a post
POST /posts/{post_id}/replies Logged in Create a reply
GET /replies/{id} Public Get a single reply
PATCH /replies/{id} Author / Moderator Update a reply
DELETE /replies/{id} Author / Moderator Delete a reply
POST /replies/{id}/accept Post author / Moderator Accept as answer

GET /posts/{post_id}/replies - parameters

Parameter Type Default Description
page int 1 Page number
per_page int 30 Max 100
sort string oldest oldest, newest, best

POST /replies/{id}/accept

Marks this reply as the accepted answer. Only the original post author or a moderator can call this. Fires the jetonomy_reply_accepted action hook and awards +15 reputation to the reply author.

await fetch( `/wp-json/jetonomy/v1/replies/${replyId}/accept`, {
    method: 'POST',
    headers: { 'X-WP-Nonce': nonce },
} );

Votes

Votes record up/down signals on Posts and Replies. Calling the endpoint again with the same direction removes the vote (toggle).

Method Route Auth Description
POST /posts/{id}/vote Logged in Cast or toggle a post vote
POST /replies/{id}/vote Logged in Cast or toggle a reply vote

Body for both vote endpoints

{ direction: 'up' }  // or 'down'

Response includes vote_score (current net score) and user_vote (the caller's current vote direction or null).


Search

Full-text search across Posts, Replies, Spaces, and Tags. Uses MySQL FULLTEXT with Boolean Mode by default. Swap to a custom search adapter (Meilisearch, Algolia, etc.) via the Adapter System. See 05-adapters.md.

Method Route Auth Description
GET /search Public Search across content types

Parameters

Parameter Type Required Default Description
q string Yes - Query string (min 2 chars)
type string - post post, reply, space, tag, all
space_id int - - Restrict to a specific space
date_from string - - ISO date YYYY-MM-DD
date_to string - - ISO date YYYY-MM-DD
author_id int - - Filter by author's WP user ID
tag string - - Filter by tag slug
sort string - relevance relevance, newest, votes

Using type=all returns a grouped response with posts, spaces, and tags keys.

const params = new URLSearchParams({
    q:        'caching strategies',
    type:     'post',
    space_id: 5,
    sort:     'votes',
} );

const res  = await fetch( `/wp-json/jetonomy/v1/search?${params}` );
const data = await res.json();
// data.data → array of matching post objects
// data.meta → { total, has_more }

Tags

Method Route Auth Description
GET /tags Public List all global tags
POST /tags Logged in (trust level 1+) Create a tag
GET /space-tags Public List tags filtered to a space

GET /space-tags - parameters

Parameter Type Description
space_id int Required. Filter tags by space.

Notifications

Method Route Auth Description
GET /notifications Logged in List notifications for current user
GET /notifications/unread-count Logged in Get unread count (cached 30s)
POST /notifications/mark-all-read Logged in Mark all notifications read
PATCH /notifications/{id} Logged in Mark a single notification read

GET /notifications - parameters

Parameter Type Default Description
per_page int 20 Max 50
unread_only bool false Return only unread notifications

Subscriptions

Subscriptions track which Spaces or Posts a user follows for new-content notifications.

Method Route Auth Description
GET /subscriptions Logged in List current user's subscriptions
DELETE /subscriptions/{id} Logged in Remove a subscription

To create a subscription, use the POST /spaces/{id}/members endpoint (joining a space auto-subscribes you) or the follow/unfollow UI action in the frontend which calls this API internally.


Moderation

All moderation endpoints require the jetonomy_moderate capability (granted to admins, editors, and users with Moderator role by default).

Method Route Auth Description
GET /moderation/queue Moderator List items pending review
POST /moderation/approve/{type}/{id} Moderator Approve a flagged item
POST /moderation/spam/{type}/{id} Moderator Mark as spam
POST /moderation/trash/{type}/{id} Moderator Send to trash
POST /moderation/bulk (new in 1.4.1) Moderator Approve / spam / trash many posts in one call
POST /flags Logged in Submit a flag on a post or reply
GET /posts/{id}/flags (new in 1.4.1) Moderator The flags raised against a specific post
GET /moderation/flags Moderator List all open flags
POST /moderation/flags/{id}/resolve Moderator Resolve a flag
POST /moderation/ban Moderator Ban a user
DELETE /moderation/ban/{id} Moderator Remove a ban

POST /moderation/bulk - body

{
    action:     'approve',  // approve | spam | trash
    object_type: 'post',    // or 'reply'
    object_ids: [101, 104, 109, 117]
}

Returns per-item results so partial failures are visible:

{
    succeeded: [101, 104, 117],
    failed:    [{ id: 109, reason: 'already_spam' }]
}

{type} in approve/spam/trash routes is either post or reply.

POST /flags - body

{
    object_type: 'post',   // or 'reply'
    object_id:   42,
    reason:      'spam',   // spam | off-topic | inappropriate | other
}

POST /moderation/ban - body

{
    user_id:  123,
    reason:   'Repeated spam',
    duration: 7,           // days - omit for permanent ban
}

Leaderboards

Method Route Auth Description
GET /leaderboards Public Get top contributors

Parameters

Parameter Type Default Description
period string week week, month, all-time
per_page int 10 Max 50
space_id int - Restrict to a space

Users

Method Route Auth Description
GET /users/me Logged in Get the current user's profile
GET /users/{id} Public Get a user's public profile
PATCH /users/{id} Owner / Admin Update a user profile
GET /users/by-login/{login} Public Look up a user by login slug
GET /users/{id}/posts Public List posts by this user

PATCH /users/{id} - updatable fields

{
    bio:         'Forum moderator and PHP developer.',
    website:     'https://example.com',
    location:    'Berlin',
    twitter:     'janedoe',
    github:      'janedoe',
    avatar_url:  'https://example.com/avatar.jpg',
}

Updates (Polling)

The Updates endpoint powers the "N new replies" banner in single-post view. It is polled periodically by the Interactivity API store.

Method Route Auth Description
GET /updates Public Check for new activity since a timestamp

Parameters

Parameter Type Description
since string ISO 8601 timestamp. Returns items created after this time.
post_id int If provided, returns new reply count for that post

Pro Endpoints

The following endpoints are available only when Jetonomy Pro is active and the relevant extension is enabled.

Private Messaging (private-messaging extension)

Method Route Auth Description
GET /conversations Logged in List conversations
POST /conversations Trust Level 1+ Start a new conversation
GET /conversations/{id} Participant Get conversation details
PATCH /conversations/{id} Participant Mute/unmute a conversation
GET /conversations/{id}/messages Participant List messages (paginated)
POST /conversations/{id}/messages Participant + TL 1+ Send a message
GET /conversations/unread-count Logged in Unread message count (30s cache)

POST /conversations - body

{
    participants: [4, 17],          // WP user IDs
    title:        'Project sync',   // Optional for group conversations
    message:      'Hey, quick question...',
}

Analytics (analytics extension)

All analytics endpoints require the jetonomy_view_analytics capability.

Method Route Description
GET /analytics/overview Daily series + period comparison
GET /analytics/top-spaces Ranked by period activity
GET /analytics/top-contributors Ranked by posts + replies
GET /analytics/engagement Engagement rate, avg reply time, unanswered %
GET /analytics/moderation Flags, bans, spam actions
GET /analytics/export CSV download

Analytics parameters (all endpoints)

Parameter Type Default Description
range string 30d 7d, 30d, 90d, custom
start string - ISO date (required when range=custom)
end string - ISO date (required when range=custom)
const res  = await fetch(
    '/wp-json/jetonomy/v1/analytics/overview?range=30d',
    { headers: { 'X-WP-Nonce': nonce } }
);
const data = await res.json();

Error Responses

All errors follow the standard WP REST API format:

{
  "code":    "rest_forbidden",
  "message": "You are not allowed to do this.",
  "data":    { "status": 403 }
}

Common error codes:

Code HTTP Meaning
rest_forbidden 403 Missing capability or nonce
rest_not_found 404 Resource does not exist
validation_error 422 Invalid or missing parameters
rate_limited 429 Too many requests from this user

What's Next?

Jetonomy exposes 53 hooks in the free plugin and 8 additional hooks in Jetonomy Pro. Every hook follows the jetonomy_ prefix convention. Use them in your theme's functions.php, a site-specific mu-plugin, or a companion plugin.

Hook naming prefix: jetonomy_ Namespace: Jetonomy\


Content Hooks

These hooks fire around the full lifecycle of posts and replies.


jetonomy_after_create_post

Fires immediately after a new post is saved successfully.

Parameters

Parameter Type Description
$post_id int ID of the newly created post
$space_id int ID of the space the post was created in

Source: includes/api/class-posts-controller.php, includes/class-abilities.php

add_action( 'jetonomy_after_create_post', function( int $post_id, int $space_id ) {
    // Push an event to your analytics pipeline.
    my_analytics_track( 'forum_post_created', [
        'post_id'  => $post_id,
        'space_id' => $space_id,
        'user_id'  => get_current_user_id(),
    ] );
}, 10, 2 );

jetonomy_after_create_reply

Fires immediately after a new reply is saved successfully. The built-in Notifier also listens to this hook to dispatch reply notifications.

Parameters

Parameter Type Description
$reply_id int ID of the newly created reply
$post_id int ID of the post being replied to

Source: includes/api/class-replies-controller.php, includes/class-abilities.php

add_action( 'jetonomy_after_create_reply', function( int $reply_id, int $post_id ) {
    // Award XP in your gamification plugin.
    my_gamification_award_xp( get_current_user_id(), 5, 'reply_created' );
}, 10, 2 );

jetonomy_post_updated

Fires after a post is updated.

Parameters

Parameter Type Description
$post_id int ID of the updated post

Source: includes/api/class-posts-controller.php

add_action( 'jetonomy_post_updated', function( int $post_id ) {
    // Bust an external cache when a post changes.
    my_cdn_purge( 'post', $post_id );
} );

jetonomy_post_deleted

Fires after a post is permanently deleted (not trashed).

Parameters

Parameter Type Description
$post_id int ID of the deleted post

Source: includes/api/class-posts-controller.php


jetonomy_reply_updated

Fires after a reply is updated.

Parameters

Parameter Type Description
$reply_id int ID of the updated reply

Source: includes/api/class-replies-controller.php


jetonomy_reply_deleted

Fires after a reply is permanently deleted.

Parameters

Parameter Type Description
$reply_id int ID of the deleted reply

Source: includes/api/class-replies-controller.php


jetonomy_reply_accepted

Fires after a reply is marked as the accepted answer. The free plugin uses this to award +15 reputation to the reply author.

Parameters

Parameter Type Description
$reply_id int ID of the accepted reply
$post_id int ID of the parent post

Source: includes/api/class-replies-controller.php

add_action( 'jetonomy_reply_accepted', function( int $reply_id, int $post_id ) {
    // Grant a badge for having a reply accepted.
    my_badges_award( get_current_user_id(), 'answer-accepted' );
}, 10, 2 );

Voting

jetonomy_after_vote

Fires after a vote is cast or changed on a post or reply.

Parameters

Parameter Type Description
$object_type string 'post' or 'reply'
$object_id int ID of the voted-on item
$direction string 'up', 'down', or 'none' (vote removed)
$user_id int The voting user's WP ID

Source: includes/api/class-votes-controller.php, includes/class-abilities.php

add_action( 'jetonomy_after_vote', function( string $type, int $id, string $direction, int $user_id ) {
    if ( 'up' === $direction && 'post' === $type ) {
        // Award XP to the post author for receiving an upvote.
        $post = \Jetonomy\Models\Post::find( $id );
        if ( $post ) {
            my_xp_award( (int) $post->author_id, 2, 'post_upvoted' );
        }
    }
}, 10, 4 );

Moderation

jetonomy_content_moderated

Fires when a moderator takes an action on a post or reply: approve, spam, or trash.

Parameters

Parameter Type Description
$action string 'approve', 'spam', or 'trash'
$object_type string 'post' or 'reply'
$object_id int ID of the moderated item
$moderator_id int WP user ID of the moderator

Source: includes/api/class-moderation-controller.php, includes/admin/class-admin.php

add_action( 'jetonomy_content_moderated', function( string $action, string $type, int $id, int $mod_id ) {
    if ( 'spam' === $action ) {
        my_spam_log( $type, $id, $mod_id );
    }
}, 10, 4 );

Trust & Reputation

jetonomy_trust_level_changed

Fires when a user's trust level is recalculated and changes. Runs from the daily cron job and the wp jetonomy recalculate-trust WP-CLI command.

Parameters

Parameter Type Description
$user_id int WP user ID
$old_level int Previous trust level (0-5)
$new_level int New trust level (0-5)

Source: includes/class-cron.php, includes/class-cli.php

add_action( 'jetonomy_trust_level_changed', function( int $user_id, int $old, int $new ) {
    if ( $new > $old ) {
        // Grant a WP capability when a user reaches Trust Level 3.
        if ( 3 === $new ) {
            $user = get_user_by( 'ID', $user_id );
            $user->add_cap( 'my_plugin_advanced_features' );
        }
    }
}, 10, 3 );

jetonomy_reputation_changed

Fires whenever a user's reputation score changes.

Parameters

Parameter Type Description
$user_id int WP user ID
$delta int Points added (positive) or removed (negative)
$reason string Machine-readable reason slug (e.g. 'post_upvoted', 'reply_accepted')

Source: includes/trust/class-reputation.php

add_action( 'jetonomy_reputation_changed', function( int $user_id, int $delta, string $reason ) {
    // Sync reputation to BuddyPress profile.
    bp_update_user_meta( $user_id, 'jetonomy_rep', \Jetonomy\Models\UserProfile::get_reputation( $user_id ) );
}, 10, 3 );

Notifications

jetonomy_notification_created

Fires after a notification is created and stored.

Parameters

Parameter Type Description
$notification_id int ID of the new notification record
$user_id int Recipient WP user ID
$type string Notification type slug (e.g. 'reply', 'mention', 'accepted')

Source: includes/notifications/class-notifier.php

add_action( 'jetonomy_notification_created', function( int $notif_id, int $user_id, string $type ) {
    // Forward notifications to a mobile push service.
    if ( 'mention' === $type ) {
        my_push_service_notify( $user_id, 'You were mentioned in a discussion.' );
    }
}, 10, 3 );

Spaces

jetonomy_user_joined_space

Fires after a user successfully joins a space.

Parameters

Parameter Type Description
$user_id int WP user ID of the new member
$space_id int ID of the space joined

Source: includes/models/class-space-member.php

add_action( 'jetonomy_user_joined_space', function( int $user_id, int $space_id ) {
    // Auto-subscribe the user to a MailChimp list tied to this space.
    my_mailchimp_subscribe( $user_id, "space_{$space_id}" );
}, 10, 2 );

Membership

These hooks fire from both the MemberPress and PMPro adapters.

jetonomy_membership_activated

Fires when a user's membership subscription becomes active.

Parameters

Parameter Type Description
$user_id int WP user ID
$level_id string Membership level identifier
$adapter string Adapter identifier (e.g. 'memberpress', 'pmpro', 'woocommerce')

Source: includes/adapters/class-member-press-adapter.php, class-pmpro-adapter.php


jetonomy_membership_deactivated

Fires when a user's membership expires or is cancelled.

Parameters

Parameter Type Description
$user_id int WP user ID
$level_id string Membership level identifier
$adapter string Adapter identifier (e.g. 'memberpress', 'pmpro', 'woocommerce')

Source: includes/adapters/class-member-press-adapter.php, class-pmpro-adapter.php

add_action( 'jetonomy_membership_deactivated', function( int $user_id, string $level_id, string $adapter ) {
    // Revoke access to private spaces when membership lapses.
    $private_spaces = \Jetonomy\Models\Space::get_by_membership_level( $level_id );
    foreach ( $private_spaces as $space ) {
        \Jetonomy\Models\SpaceMember::remove( $user_id, $space->id );
    }
}, 10, 3 );

Topic Management

jetonomy_post_merged

Fires after two posts are merged (all replies moved to the target, source deleted).

Parameters

Parameter Type Description
$source_post_id int The post that was merged and deleted
$target_post_id int The post that received the replies

jetonomy_reply_split

Fires after a reply is split into a new standalone post.

Parameters

Parameter Type Description
$new_post_id int ID of the newly created post
$original_reply_id int ID of the reply that was split out

Template Hooks

These hooks fire inside the PHP templates and let you inject content without overriding template files.

jetonomy_before_content

Fires inside the .jt-app wrapper, before the header partial and content container. Bridge plugins (such as BuddyNext) use this to inject a community subnav in place of the default Jetonomy community nav.

Parameters

Parameter Type Description
$data array Route data: ['route' => string, 'slug' => string]

Source: includes/class-template-loader.php

add_action( 'jetonomy_before_content', function( array $data ) {
    echo '<div class="my-subnav">Custom nav here</div>';
} );

jetonomy_after_content

Fires after the main .container closes, before the .jt-app wrapper closes.

Parameters

Parameter Type Description
$data array Route data

jetonomy_new_post_fields

Fires inside the new-post form, after the built-in fields. Use this to inject custom form fields (e.g. for Pro custom fields extension).

Source: templates/views/new-post.php

add_action( 'jetonomy_new_post_fields', function() {
    // Render an additional "Estimated time" field.
    echo '<label for="jt-estimated-time">' . esc_html__( 'Estimated time (hours)', 'my-plugin' ) . '</label>';
    echo '<input type="number" id="jt-estimated-time" name="estimated_time" min="0" />';
} );

jetonomy_post_meta_fields

Fires inside the single-post view, after the post meta line.

Source: templates/views/single-post.php


jetonomy_post_actions

Fires inside the single-post view, inside the post action toolbar.

Source: templates/views/single-post.php


jetonomy_reply_actions

Fires inside the reply card, inside the reply action row.

Source: templates/partials/reply-card.php


jetonomy_profile_after_stats

Fires on user profile pages, after the reputation/trust stats block.

Source: templates/views/user-profile.php


jetonomy_profile_display_fields

Fires on user profile pages in the display (read-only) section. Use this to render extra profile fields.

Source: templates/views/user-profile.php

add_action( 'jetonomy_profile_display_fields', function() {
    $user_id = get_queried_object_id(); // or extract from the URL
    $company = get_user_meta( $user_id, 'company', true );
    if ( $company ) {
        printf( '<p class="jt-profile-field"><strong>%s</strong> %s</p>',
            esc_html__( 'Company:', 'my-plugin' ),
            esc_html( $company )
        );
    }
} );

jetonomy_profile_edit_fields

Fires inside the edit-profile form. Pair with jetonomy_profile_display_fields and a custom save_post / REST action to persist data.

Source: templates/views/edit-profile.php


jetonomy_header_nav_items

Fires inside the community header, after the built-in nav items. Add extra navigation links here.

Source: templates/partials/header.php

add_action( 'jetonomy_header_nav_items', function() {
    echo '<a href="/community/events/" class="jt-nav-link">Events</a>';
} );

jetonomy_sidebar_before

Fires at the top of the Jetonomy sidebar, before any widgets render. Prime slot for ad plugins, announcement banners, or custom cards.

Parameters

Parameter Type Description
$space object|null Current space object, or null outside a space

Source: templates/partials/sidebar.php

add_action( 'jetonomy_sidebar_before', function( $space ) {
    echo do_shortcode( '[wb_ads position="jetonomy_sidebar_top"]' );
} );

jetonomy_sidebar_after

Fires at the bottom of the Jetonomy sidebar, after all widgets render.

Parameters

Parameter Type Description
$space object|null Current space object, or null outside a space

Source: templates/partials/sidebar.php

add_action( 'jetonomy_sidebar_after', function( $space ) {
    echo do_shortcode( '[wb_ads position="jetonomy_sidebar_bottom"]' );
} );

jetonomy_sidebar_after_about

Fires in the sidebar immediately after the "About" space card closes. Only fires when a space is present (i.e. on space-scoped pages). Ideal for ads, announcements, or CTAs pinned below the space intro, before Trending and other widgets.

Parameters

Parameter Type Description
$space object Current space object

Source: templates/partials/sidebar.php

add_action( 'jetonomy_sidebar_after_about', function( $space ) {
    echo do_shortcode( '[wbam_ad id="42"]' );
} );

jetonomy_after_post_article

Fires after the main post <article> element and before the replies section on a single post view. Ideal for ads, related-topic blocks, or CTAs between the topic body and the reply list.

⚠️ Do not confuse with the jetonomy_after_post_content filter (below) which fires inside the post body. This hook is an action named _article to avoid collision.

Parameters

Parameter Type Description
$post object Current post object

Source: templates/views/single-post.php

add_action( 'jetonomy_after_post_article', function( $post ) {
    echo do_shortcode( '[wb_ads position="jetonomy_after_topic"]' );
} );

jetonomy_before_replies

Fires inside .jt-replies-section, immediately before the replies list (above both the empty state and the populated list).

Parameters

Parameter Type Description
$post object Current post object
$total_replies int Total reply count for the post

Source: templates/views/single-post.php

add_action( 'jetonomy_before_replies', function( $post, $total_replies ) {
    if ( $total_replies > 5 ) {
        echo do_shortcode( '[wb_ads position="jetonomy_replies_top"]' );
    }
}, 10, 2 );

jetonomy_between_replies

Fires after each top-level reply in the reply list. Use the $index parameter to inject content every Nth reply (e.g. an ad every 5 replies).

Parameters

Parameter Type Description
$reply object The reply object just rendered
$index int Zero-based index within the current batch (first batch or last batch)
$post object Current post object

Source: templates/views/single-post.php

The replies list renders in two batches (opening + latest). The index resets at the start of each batch. Use $reply->id for absolute identity.

add_action( 'jetonomy_between_replies', function( $reply, $index, $post ) {
    // Inject an ad after every 5th reply.
    if ( 4 === $index % 5 ) {
        echo '<div class="jt-reply-ad">' . do_shortcode( '[wb_ads position="jetonomy_between_replies"]' ) . '</div>';
    }
}, 10, 3 );

jetonomy_after_replies

Fires inside .jt-replies-section, after the replies list and before the composer.

Parameters

Parameter Type Description
$post object Current post object
$total_replies int Total reply count for the post

Source: templates/views/single-post.php

add_action( 'jetonomy_after_replies', function( $post, $total_replies ) {
    echo do_shortcode( '[wb_ads position="jetonomy_replies_bottom"]' );
}, 10, 2 );

Admin Extension Hooks

Use these to add content to the Jetonomy admin pages without overriding core admin files.

Hook Parameters Where it fires
jetonomy_admin_dashboard_widgets none Dashboard page - add custom stat cards
jetonomy_admin_dashboard_after_stats none Dashboard - below the stats row
jetonomy_admin_settings_tabs none Settings page - register new tab nav items
jetonomy_admin_settings_tab_content $active_tab (string) Settings page - render tab content
jetonomy_admin_moderation_tabs none Moderation page - extra tab nav items
jetonomy_admin_moderation_tab_content $active_tab (string) Moderation page - render tab content
jetonomy_admin_space_edit_tabs $space_id (int) Space edit page - extra tab nav items
jetonomy_admin_space_edit_tab_content $active_tab (string), $space_id (int) Space edit page - render tab content
jetonomy_admin_render_extensions none Admin - Extensions tab placeholder
jetonomy_admin_render_license none Admin - License tab placeholder

Example: adding a Settings tab

// Register the tab nav item.
add_action( 'jetonomy_admin_settings_tabs', function() {
    $active = $_GET['tab'] ?? 'general';
    $class  = 'my-custom' === $active ? 'nav-tab-active' : '';
    printf(
        '<a href="?page=jetonomy-settings&tab=my-custom" class="nav-tab %s">%s</a>',
        esc_attr( $class ),
        esc_html__( 'My Settings', 'my-plugin' )
    );
} );

// Render the tab content.
add_action( 'jetonomy_admin_settings_tab_content', function( string $active_tab ) {
    if ( 'my-custom' !== $active_tab ) {
        return;
    }
    echo '<div class="jt-settings-card">';
    echo '<div class="jt-settings-card__head">';
    echo '<p class="jt-settings-card__title">My Settings</p>';
    echo '</div>';
    // Your settings form here.
    echo '</div>';
} );

Performance & Cron

jetonomy_cron_batch_size

Filters the maximum number of rows processed per run by any of Jetonomy's background cleanup handlers. The default is 500 rows per run, which prevents cron jobs from timing out on large sites with high activity volumes.

Parameters

Parameter Type Description
$batch_size int Maximum rows to process. Default: 500
$handler string The handler name, e.g. 'prune_activity_log', 'expire_restrictions', 'cleanup_notifications', 'publish_scheduled_posts'

Return: int

Source: includes/class-cron.php

Use the $handler parameter to set different limits per job:

add_filter( 'jetonomy_cron_batch_size', function( int $batch_size, string $handler ): int {
    // Allow the activity log pruner to work through larger batches on this high-traffic site.
    if ( 'prune_activity_log' === $handler ) {
        return 1000;
    }
    return $batch_size;
}, 10, 2 );

Filter Hooks

jetonomy_template_map

Filters the route-to-template map used by Template_Loader. Pass an absolute path to override an existing template or add a completely new route. See Template Overrides for the complete guide.

Parameters

Parameter Type Description
$map array ['route' => 'relative/path.php']

Return: array Modified map

add_filter( 'jetonomy_template_map', function( array $map ): array {
    // Register a new 'events' route resolved against the Pro plugin directory.
    $map['events'] = MYPLUGIN_DIR . 'templates/events.php';
    return $map;
} );

jetonomy_check_content

Filters content before it is saved as a post or reply. Return a WP_Error to reject the content with a message shown to the user.

Parameters

Parameter Type Description
$result true|WP_Error Pass through or return a WP_Error to block
$content string The sanitized HTML content string
$user_id int Author's WP user ID

Return: true|WP_Error

Source: includes/api/class-posts-controller.php, class-replies-controller.php

add_filter( 'jetonomy_check_content', function( $result, string $content, int $user_id ) {
    // Block posts containing a forbidden phrase.
    if ( str_contains( strtolower( $content ), 'buy cheap followers' ) ) {
        return new WP_Error( 'spam_blocked', __( 'This content was flagged as spam.', 'my-plugin' ) );
    }
    return $result;
}, 10, 3 );

jetonomy_after_post_content

Filters output rendered after the main post content area in single-post view. Return an HTML string.

Parameters

Parameter Type Description
$html string HTML to render after post content (empty by default)
$post \Jetonomy\Models\Post The current post object

Return: string

Source: templates/views/single-post.php

add_filter( 'jetonomy_after_post_content', function( string $html, $post ): string {
    $html .= '<div class="my-related-posts">' . my_get_related_posts( $post->id ) . '</div>';
    return $html;
}, 10, 2 );

jetonomy_notification_email_headers

Filters the email headers array passed to wp_mail() for all Jetonomy notifications.

Parameters

Parameter Type Description
$headers array Array of mail headers

Return: array

Source: includes/adapters/class-wp-mail-adapter.php

add_filter( 'jetonomy_notification_email_headers', function( array $headers ): array {
    $headers[] = 'Reply-To: noreply@example.com';
    return $headers;
} );

jetonomy_profile_url

Filters the public URL for a user's community profile.

Parameters

Parameter Type Description
$url string Default profile URL (e.g. /community/u/janedoe/)
$user_id int WP user ID

Return: string

Source: includes/functions.php

add_filter( 'jetonomy_profile_url', function( string $url, int $user_id ): string {
    // Point profile links to a BuddyPress profile instead.
    $bp_url = bp_core_get_user_domain( $user_id );
    return $bp_url ?: $url;
}, 10, 2 );

jetonomy_admin_menu_label

Filters the top-level admin menu label.

Return: string

add_filter( 'jetonomy_admin_menu_label', fn() => 'Forum' );

jetonomy_admin_menu_icon

Filters the Dashicons icon for the admin menu item.

Return: string (Dashicons class, e.g. 'dashicons-format-chat')


jetonomy_show_community_nav

Filters whether the built-in community nav bar is displayed. Return false to hide it (useful when a bridge plugin provides its own nav).

Parameters

Parameter Type Description
$show bool true by default

Return: bool


jetonomy_importers

Filters the list of registered importers shown in the Import tool.

Parameters

Parameter Type Description
$importers array ['id' => Importer_Instance]

Return: array

Source: includes/import/class-import-manager.php

add_filter( 'jetonomy_importers', function( array $importers ): array {
    $importers['my-forum'] = new My_Forum_Importer();
    return $importers;
} );

jetonomy_search_query_args

Fires inside the Search controller before the SQL is built. Use this to modify search parameters.

Parameters

Parameter Type Description
$args array Keys: q, space_id, date_from, date_to, author_id, tag_slug, sort

Return: array

Source: includes/api/class-search-controller.php


Pro Hooks

These hooks are available only when Jetonomy Pro is active. Pro injects into core admin via the standard admin hooks (jetonomy_admin_dashboard_widgets, jetonomy_admin_settings_tabs) and registers its own extension lifecycle events.

Hook Type Description
jetonomy_pro_extension_booted action Fires after a Pro extension's boot() runs. Params: $extension_id (string)
jetonomy_pro_extension_enabled action Fires when an extension is toggled on in admin. Params: $extension_id (string)
jetonomy_pro_extension_disabled action Fires when an extension is toggled off. Params: $extension_id (string)
jetonomy_pro_message_sent action Fires after a private message is sent. Params: $message_id (int), $conversation_id (int), $sender_id (int)
jetonomy_pro_reaction_added action Fires when a reaction is added. Params: $object_type (string), $object_id (int), $emoji (string), $user_id (int)
jetonomy_pro_poll_vote_cast action Fires when a poll vote is cast. Params: $poll_id (int), $option_id (int), $user_id (int)
jetonomy_pro_webhook_sent action Fires after a webhook is dispatched. Params: $webhook_id (int), $event (string), $response_code (int)
jetonomy_pro_digest_sent action Fires after an email digest is sent. Params: $user_id (int), $frequency (string)

What's Next?

Jetonomy's template system is designed to be overridden without touching plugin files. Place your custom templates in your theme and they will be loaded automatically - no hooks, no filters required for simple overrides.


How It Works

When Jetonomy loads a template, Template_Loader checks your active theme directory first. If a matching file exists there, it loads that file instead of the plugin's copy. This means your overrides survive plugin updates.

Resolution order:

  1. {your-theme}/jetonomy/{relative-path} - checked first
  2. {jetonomy-plugin}/templates/{relative-path} - fallback default

The {relative-path} is always the same path the plugin uses internally - for example, views/home.php or partials/reply-card.php.


Directory Structure

Create the following folder structure in your active theme:

your-theme/
└── jetonomy/
    ├── views/
    │   ├── home.php
    │   ├── category.php
    │   ├── space.php
    │   ├── space-members.php
    │   ├── space-roadmap.php
    │   ├── single-post.php
    │   ├── new-post.php
    │   ├── user-profile.php
    │   ├── edit-profile.php
    │   ├── leaderboard.php
    │   ├── notifications.php
    │   ├── moderation.php
    │   ├── search.php
    │   ├── tag.php
    │   └── invite.php
    └── partials/
        ├── avatar.php
        ├── breadcrumb.php
        ├── composer.php
        ├── header.php
        ├── pagination.php
        ├── post-card.php
        ├── reply-card.php
        └── sidebar.php

You do not need to copy all of these. Only create the files you want to customize - any file not present in your theme directory is loaded from the plugin.


Available Templates

Views (full-page templates)

File Route URL Pattern
views/home.php home /community/
views/category.php category /community/category/{slug}/
views/space.php space /community/s/{slug}/
views/space-members.php space-members /community/s/{slug}/members/
views/space-roadmap.php space-roadmap /community/s/{slug}/roadmap/
views/single-post.php post /community/s/{slug}/t/{slug}/
views/new-post.php new-post /community/s/{slug}/new/
views/user-profile.php profile /community/u/{login}/
views/edit-profile.php edit-profile /community/u/{login}/edit/
views/leaderboard.php leaderboard /community/leaderboard/
views/notifications.php notifications /community/notifications/
views/moderation.php moderation /community/mod/
views/search.php search /community/search/
views/tag.php tag /community/tag/{slug}/
views/invite.php invite /community/invite/{code}/

Partials (reusable template fragments)

File Rendered via
partials/header.php Loaded at the top of every community page
partials/sidebar.php Loaded in space and home views
partials/breadcrumb.php Loaded in space, category, and post views
partials/post-card.php Iterated over in space and home views
partials/reply-card.php Iterated over in single-post view
partials/pagination.php Loaded at the bottom of listing pages
partials/avatar.php Used inside post-card, reply-card, and profile views
partials/composer.php Rich-text composer used in new-post and reply forms

Step-by-Step: Overriding the Post Card

This example adds a "Sponsored" badge to posts from a specific space.

Step 1: Copy the original template

Copy wp-content/plugins/jetonomy/templates/partials/post-card.php to:

your-theme/jetonomy/partials/post-card.php

Step 2: Modify your copy

Open your-theme/jetonomy/partials/post-card.php and add your customization. The variable $post (a \Jetonomy\Models\Post object) is available inside the partial.

<?php
// your-theme/jetonomy/partials/post-card.php

// The $post variable is passed via Template_Loader::partial()
$is_sponsored = ( (int) $post->space_id === MY_SPONSORED_SPACE_ID );
?>

<div class="jt-row <?php echo $is_sponsored ? 'jt-row--sponsored' : ''; ?>">
    <?php if ( $is_sponsored ) : ?>
        <span class="my-sponsored-badge">Sponsored</span>
    <?php endif; ?>

    <?php
    // Continue with the original template output.
    // Tip: call Template_Loader::partial() for nested partials so they also
    // respect theme overrides.
    \Jetonomy\Template_Loader::partial( 'avatar', [ 'user_id' => $post->author_id ] );
    ?>

    <div class="jt-row__body">
        <a href="<?php echo esc_url( \Jetonomy\post_url( $post ) ); ?>">
            <?php echo esc_html( $post->title ); ?>
        </a>
    </div>
</div>

Step 3: Add styles

Add CSS to your theme for the new .jt-row--sponsored class. Reference Jetonomy's CSS tokens so your styles adapt to dark mode automatically:

/* your-theme/style.css or an enqueued stylesheet */

.jt-row--sponsored {
    border-left: 3px solid var(--jt-accent);
}

.my-sponsored-badge {
    display: inline-block;
    font-size: 0.75rem;
    color: var(--jt-text-secondary);
    background: var(--jt-accent-light);
    border-radius: var(--jt-radius-sm);
    padding: 2px 6px;
}

Calling Partials from Templates

Inside any view or partial, use Template_Loader::partial() instead of a raw include. This ensures the theme-override check runs for nested partials too:

// Inside your custom view or partial:
\Jetonomy\Template_Loader::partial( 'pagination', [
    'total'    => $total,
    'per_page' => 20,
    'page'     => $current_page,
] );

The second argument is passed as local variables to the partial (via extract()).


Available Variables in Templates

Templates receive route data via $data (array with route and slug keys). Most views also query the database directly at the top of the file. When you copy a template, review the top of the plugin's original file to understand which variables are set before the HTML output begins.

Common objects available in plugin templates:

Variable Type Available in
$data array All views
$space \Jetonomy\Models\Space space, space-members, new-post
$post \Jetonomy\Models\Post single-post, post-card partial
$reply \Jetonomy\Models\Reply reply-card partial
$user WP_User user-profile, edit-profile
$posts array home, space, search, tag
$settings array All views (from get_option('jetonomy_settings'))

The jetonomy_template_map Filter

For cases where you need to register a completely new route - or override the template for an existing route with a file stored outside the theme - use the jetonomy_template_map filter.

add_filter( 'jetonomy_template_map', function( array $map ): array {
    // Override an existing route with an absolute path.
    $map['leaderboard'] = get_stylesheet_directory() . '/jetonomy/views/leaderboard.php';

    // Register a brand-new route (accessible at /community/events/).
    $map['events'] = MY_PLUGIN_DIR . 'templates/community-events.php';

    return $map;
} );

Important: If you provide an absolute path (starting with /), the theme-override check is bypassed - the file you point to is loaded directly. This is the correct approach for Pro extensions and companion plugins that ship their own templates.

The Jetonomy Router must know about new routes before they can receive traffic. Register rewrite rules alongside the template map:

// Register a custom rewrite rule for /community/events/.
add_action( 'init', function() {
    $settings  = get_option( 'jetonomy_settings', [] );
    $base_slug = $settings['base_slug'] ?? 'community';

    add_rewrite_rule(
        '^' . preg_quote( $base_slug, '^' ) . '/events/?$',
        'index.php?jetonomy_route=events',
        'top'
    );
} );

// Teach WordPress to recognize the query var.
add_filter( 'query_vars', function( array $vars ): array {
    $vars[] = 'jetonomy_route';
    return $vars;
} );

After adding rewrite rules, flush permalinks once: go to Settings → Permalinks and click Save Changes, or run:

wp --path="/path/to/wordpress" rewrite flush

Child Theme Compatibility

If you are using a child theme, place overrides in the child theme directory - get_stylesheet_directory() resolves to the child theme path when a child theme is active. The plugin does not check the parent theme separately, so all overrides must live in the active (child) theme.


Pro Template Overrides

Jetonomy Pro registers its own templates for Private Messaging via the jetonomy_template_map filter using absolute paths. You can override Pro templates in your theme using the same directory structure:

your-theme/
└── jetonomy/
    └── views/
        ├── messages.php      # /community/messages/
        └── conversation.php  # /community/messages/{id}/

Place overrides here and they will be detected automatically because Pro's Template_Loader::partial() calls still respect the theme directory check for non-absolute paths.


What's Next?

Jetonomy includes seven shortcodes, four classic widgets, and eight Gutenberg blocks so you can embed community content anywhere on your WordPress site - sidebars, pages, posts, or block-based layouts.

What You Will Learn

  • How to use the seven built-in shortcodes and their attributes
  • How to add the four classic widgets to sidebar areas
  • How to insert the eight Gutenberg blocks in the block editor
  • How shortcodes and blocks share the same rendering logic

At a Glance

Type Slug Purpose Since
Shortcode [jetonomy_recent_posts] Recent posts feed 1.0
Shortcode [jetonomy_trending_posts] Hot-scored trending posts 1.3.0
Shortcode [jetonomy_spaces] Space directory grid 1.0
Shortcode [jetonomy_leaderboard] Top members by reputation 1.0
Shortcode [jetonomy_user_profile] Single user profile card 1.2
Shortcode [jetonomy_space_members] Members of one space 1.2
Shortcode [jetonomy_compose_topic] New in 1.3.7 - inline topic composer (fixed space or member picker) 1.3.7
Block jetonomy/forum-feed Live post feed 1.3.0
Block jetonomy/trending Trending topics with time-decayed hot score 1.3.6
Block jetonomy/space-list Space grid 1.3.0
Block jetonomy/leaderboard Top members 1.3.0
Block jetonomy/navigation Permission-aware category + space tree 1.3.5
Block jetonomy/user-panel Logged-in sidebar profile card 1.3.5
Block jetonomy/login Inline login/register panel 1.3.5
Block jetonomy/compose-topic New in 1.3.7 - topic composer embeddable anywhere 1.3.7

Shortcodes

All shortcodes are registered by Jetonomy\Shortcodes::register() and are available on any page or post.

Self-styling on any page (1.4.0+): every shortcode auto-enqueues assets/css/blocks.css at render time and ships its own --jtb-* token block, so a bare [jetonomy_recent_posts] paste on a regular WordPress page renders fully styled - no need to put it inside a Forum Feed block container. Works in classic editor, page builders (Elementor, Divi, Bricks, WPBakery), widget areas, and template parts.


[jetonomy_recent_posts]

Displays a list of the most recent published posts across your community or within a specific space.

Attributes

Attribute Type Default Description
count int 5 Number of posts to display
space_id int 0 Restrict to a space. 0 = all spaces
sort string latest latest or votes
[jetonomy_recent_posts count="5" space_id="3" sort="votes"]

Each post card shows the title, author name, space name, time ago, vote score, and reply count.


[jetonomy_trending_posts]

Displays a ranked list of "hot" posts using a time-decayed score of recent votes + replies.

Attributes

Attribute Type Default Description
count int 5 Number of posts to display
space_id int 0 Restrict to a space. 0 = all spaces
window int 7 Days of history used for the hot score
[jetonomy_trending_posts count="10" window="14"]

[jetonomy_spaces]

Displays a list of public, active spaces, ordered by post count.

Attributes

Attribute Type Default Description
count int 6 Number of spaces to display
category_id int 0 Filter by category. 0 = all categories
[jetonomy_spaces count="6" category_id="2"]

Each space card shows the title, a short description excerpt, and the post count.


[jetonomy_leaderboard]

Displays a ranked list of the top community members by reputation score.

Attributes

Attribute Type Default Description
count int 10 Number of members to display
[jetonomy_leaderboard count="10"]

[jetonomy_user_profile]

Displays a compact profile card for a specific user or the currently logged-in user.

Attributes

Attribute Type Default Description
user_id int 0 Target user. 0 = current logged-in user
[jetonomy_user_profile user_id="0"]

The card shows the display name, trust level badge, bio excerpt, reputation score, and post count.


[jetonomy_space_members]

Displays a list of members for a specific space, ordered by reputation.

Attributes

Attribute Type Default Description
space_id int - Required. ID of the space
count int 10 Number of members to display
[jetonomy_space_members space_id="5" count="20"]

[jetonomy_compose_topic] (new in 1.3.7)

Lets signed-in members start a new topic from any WordPress page, post, or page-builder canvas. Two modes: lock to one space, or show a picker of spaces the current user is a member of.

Attributes

Attribute Type Default Description
mode string picker picker (show a space select) or fixed (post to one space)
space_id int 0 Space ID to post into. Only used when mode="fixed". Invalid IDs degrade to picker at render time.
types CSV topic,question,idea Allowed post types for this embed
[jetonomy_compose_topic mode="picker"]
[jetonomy_compose_topic mode="fixed" space_id="5"]

Compose Topic shortcode rendered on a regular WordPress page

Behavior

  • Logged-out viewers see a "Sign in to start a new topic" CTA that redirects back to the current URL after login - no form exposure, no wasted scroll.
  • Picker mode queries Permission_Engine::can($uid, 'create_posts', $space_id) against every space the user is a member of. Only spaces where they actually have posting rights appear.
  • Fixed mode hides the picker entirely. If the hardcoded space_id is missing or the user cannot post in it, the embed silently degrades to picker mode rather than breaking.
  • Assets (blocks.css + the Interactivity API bundle) enqueue on-demand at render time so pages that don't use the shortcode carry no overhead. Works inside page builders that render shortcodes outside the_content (Elementor, Divi, Bricks, WPBakery).

Companion REST endpoint: GET /jetonomy/v1/spaces?postable_by_me=1 returns the user's postable spaces.

When the title is filled but the body is empty, an inline error banner appears above the title - no silent failures, no lost input:

Inline validation banner when the body is empty


Classic Widgets

Jetonomy registers four classic widgets for use in any theme widget area. Each widget is configured through the standard WordPress widget admin screen or the Customizer.

Recent Posts Widget

Displays recent forum posts in any sidebar or widget area.

Settings: Title, Count, Space (optional filter), Sort order

Leaderboard Widget

Displays top community contributors ranked by reputation.

Settings: Title, Count

Active Spaces Widget

Displays the most active spaces by post count.

Settings: Title, Count

User Stats Widget

Displays the currently logged-in user's stats: reputation, post count, reply count, and trust level.

Settings: Title (no other configuration - always reflects the current user)

[jetonomy_widget] shortcode (new in 1.4.0)

Each classic widget above can also be embedded directly into any page or page-builder canvas - without dropping into the Customizer or a sidebar - using the [jetonomy_widget] shortcode.

Attributes

Attribute Type Required Description
id string yes Widget id: jetonomy_recent_posts, jetonomy_leaderboard, jetonomy_active_spaces, or jetonomy_user_stats
title string no Override the widget's title (otherwise the widget's saved Customizer title or the default)
count int no For Recent Posts, Leaderboard, and Active Spaces
space_id int no For Recent Posts, optional space filter
sort string no For Recent Posts: latest or votes
[jetonomy_widget id="jetonomy_recent_posts" count="8" sort="latest"]
[jetonomy_widget id="jetonomy_leaderboard" count="10"]
[jetonomy_widget id="jetonomy_active_spaces" count="6"]
[jetonomy_widget id="jetonomy_user_stats" title="Your stats"]

Internally [jetonomy_widget] wraps WordPress core's the_widget() so the rendered markup matches what the same widget would output in a sidebar - same hooks, same CSS classes, same i18n. Useful for landing pages, footer columns, and page-builder canvases where the Customizer's sidebar widget area isn't available.


Gutenberg Blocks

Jetonomy registers eight server-side rendered blocks. Each block uses a render_callback that delegates to the matching shortcode (for most) or a dedicated render component (for Navigation, User Panel, and Login). Output is consistent wherever they appear.

All blocks live in the Widgets category of the block inserter and answer to the search term jet.

Backend (editor) vs frontend (published) render (1.4.0+)

The two surfaces render differently - by design.

  • Backend (block editor) - each block paints a framed static preview card with a "JETONOMY" pill badge, the block title, and an attribute-aware hint that reflects the current settings (Filtered to space #3, 7 day window, All public spaces, etc.). No REST calls fire. Dropping a block onto a page is instant; switching between Visual and Code editors does not trigger a network roundtrip. The preview is a mock - what the editor shows is not the actual queried data.
  • Frontend (published page) - the block's PHP render_callback runs against the live database on every request. Output is the same markup the matching [jetonomy_*] shortcode produces, with the block's wp-block-jetonomy-* wrapper class applied. Output is permission-aware: private spaces, banned authors, and silenced posts are filtered exactly as they would be on a regular community page.

Reason for the split: a live-data preview in the editor would (a) hammer the REST API on every keystroke in the title field, (b) leak permission-gated content to anyone who can edit the post, and (c) require a working REST connection at edit time. The static-card pattern mirrors what core blocks like Latest Posts do at the editor level, with the exception that core's render path can hit useEntityRecords() cheaply because the data is already cached client-side.

Each block exposes its settings through Inspector controls in the right sidebar, sized at the WordPress 6.7+ default (40px) with no deprecated bottom margins. The controls map one-to-one to the block attributes documented per block below.

Block inserter visibility was tightened in 1.4.0 - every block now registers an editor-side script (assets/js/blocks-editor.js) so all eight blocks appear in the inserter at once. Pre-1.4.0 only Compose Topic was visible because it was the only block carrying its own editor_script.

jetonomy/forum-feed

Renders a live post feed from a selected space or all spaces.

Block Attributes

Attribute Type Default Description
count number 5 Posts to show
spaceId number 0 Space ID (0 = all spaces)
sort string latest latest or votes
showHeader boolean false Render a space header above the feed
title string '' Custom title when showHeader is on

jetonomy/trending (1.3.6+)

Renders a ranked list of hot topics using a time-decayed score of recent engagement. Same render_callback plumbing as [jetonomy_trending_posts].

Block Attributes

Attribute Type Default Description
count number 5 Posts to show
spaceId number 0 Restrict to a space (0 = all)
window number 7 Days of history for the hot score
showHeader boolean true Render a "Trending" header
title string '' Custom title

jetonomy/space-list

Renders a list of community spaces. Supports category filtering.

Block Attributes

Attribute Type Default Description
count number 6 Spaces to show
categoryId number 0 Filter by category (0 = all)

jetonomy/leaderboard

Renders a leaderboard of top community members by reputation.

Block Attributes

Attribute Type Default Description
count number 10 Members to show

jetonomy/navigation (1.3.5+)

Renders the Category → Space tree as permission-aware sidebar navigation. Designed for the community sidebar of any block theme or widget area.

Why this block exists

Most community themes render the space list with a hand-maintained nav menu. That list rots the moment you add a space, and it leaks private spaces to anonymous viewers. This block queries the live category/space tree on every render and honors Jetonomy's permission layer, so private spaces stay hidden from viewers who do not have access.

Block Attributes

Attribute Type Default Description
showCategoryHeadings boolean true Group spaces by parent category
collapsible boolean false Collapsible category headings
showPostCount boolean false Show topic count next to each space
hideEmptyCategories boolean true Hide categories that have no visible spaces
title string '' Optional wrapper title

Scales to sites with thousands of spaces - the rendered tree uses Jetonomy's cached category/space index, not a per-request DB scan.


jetonomy/user-panel (1.3.5+)

Renders a compact profile card for logged-in viewers - avatar, display name, notifications count, quick links to Profile / Notifications / Messages / Edit Profile / Logout. Empty for logged-out viewers so the sidebar layout doesn't shift.

Block Attributes

Attribute Type Default Description
title string '' Optional wrapper title above the card

Auto-injects at the top of the community sidebar for logged-in viewers so admins don't need to add it by hand (disable with add_filter( 'jetonomy_sidebar_auth_card', '__return_false' )).


jetonomy/login (1.3.5+)

Renders an inline login and register panel for the community sidebar. Logged-out viewers see Login and Register tabs without leaving the page. Logged-in viewers get nothing rendered - no layout shift when state changes.

Block Attributes

Attribute Type Default Description
title string '' Header above the tabs
showRegister boolean true Show the Register tab alongside Login (honours users_can_register)

Security

  • Both forms are nonce-protected (wp_ajax_jetonomy_quick_login / wp_ajax_jetonomy_quick_register)
  • Failed login attempts are rate-limited via Jetonomy\Security\Rate_Limiter (5 attempts / 15 minutes per IP)
  • Registration respects whatever users_can_register is set to in your WP admin and any anti-spam adapter you have active (Akismet, AI spam detection)

jetonomy/compose-topic (new in 1.3.7)

Gutenberg equivalent of [jetonomy_compose_topic]. Drop it on any page, post, or template part and signed-in members can start a topic without leaving the page.

Block Attributes

Attribute Type Default Description
mode string picker picker or fixed
spaceId number 0 Space ID (fixed mode only)
types string topic,question,idea Allowed post types

Editor experience

Compose Topic block in the Gutenberg editor

  • The block editor shows a static preview (no live REST calls) - safe to drop into any page without hitting the server.
  • Inspector controls: Mode select (picker / fixed), Space ID (visible only when Mode is fixed), Allowed types (comma-separated).
  • Falls back to picker mode at render time if the fixed spaceId doesn't resolve to a space the viewer can post in - so themes/pages that were built before a space was deleted keep working instead of 500'ing.

Rendering

  • Server render delegates to [jetonomy_compose_topic], so the block + shortcode output are pixel-identical.
  • Styles come from assets/css/blocks.css - self-contained, inherits theme tokens through --wp--preset--* fallbacks so it looks correct outside Jetonomy templates.
  • Built-in mobile breakpoint at 640px - submit button spans the column width, actions stack vertically.

CSS Classes for Styling

All shortcode and block output uses the jt-shortcode CSS class prefix so you can style them in your theme without affecting core community pages:

Class Element
.jt-shortcode Wrapper on all shortcode output
.jt-shortcode-recent-posts Recent posts container
.jt-shortcode-post Individual post card
.jt-shortcode-post-title Post title link
.jt-shortcode-post-meta Author, space, and time line
.jt-shortcode-post-stats Vote and reply counts
.jt-shortcode-spaces Spaces container
.jt-shortcode-space Individual space card
.jt-shortcode-space-desc Space description excerpt
.jt-shortcode-space-stats Space post-count line
.jt-shortcode-trending-post Trending post row
.jt-shortcode-trending-rank Trending rank badge (1, 2, 3 …)
.jt-shortcode-trending-body Trending row body (title + meta + stats)
.jt-shortcode-leaderboard Leaderboard container
.jt-shortcode-rep Reputation pill in leaderboard / member rows
.jt-shortcode-profile-card User profile card
.jt-shortcode-profile-stats Reputation + post-count line on profile card
.jt-shortcode-members Members list container
.jt-shortcode-member Individual member row
.jt-shortcode-empty Empty state message
.jt-compose-topic-embed Compose-topic shortcode/block wrapper
.jt-compose-topic-embed.jt-compose-topic-login Logged-out sign-in CTA variant
.jt-compose-topic-field Label + input group
.jt-compose-topic-space Space picker <select>
.jt-compose-topic-title Title <input>
.jt-compose-topic-body Details <textarea>
.jt-compose-topic-submit Post topic button
.jt-compose-topic-error Inline error banner (shown via state.submitError)
.jt-compose-topic-posting-to "Posting in …" line in fixed mode

Building Companion Shortcodes or Blocks

If you are building a companion plugin that needs to query Jetonomy data, guard your code with a class existence check:

if ( ! defined( 'JETONOMY_VERSION' ) ) {
    return;
}

Use the model classes for server-side rendering or the REST API for client-side fetches. See the REST API Reference for available endpoints.


What's Next?

Jetonomy uses a universal adapter pattern for every external integration point. Instead of hard-coding a dependency on a specific search engine, email provider, membership plugin, or real-time service, each integration is represented by a PHP interface. You implement the interface, register your adapter, and Jetonomy uses it everywhere.

All adapters are managed through the static Adapter_Registry class (includes/adapters/class-adapter-registry.php).


The Four Adapter Types

Interface Class (namespace Jetonomy\Adapters\) What it controls
Search_Adapter interface-search-adapter.php Full-text search for posts, replies, spaces
Email_Adapter interface-email-adapter.php Outbound notification emails
Membership_Adapter interface-membership-adapter.php Membership level checks and gating
Realtime_Adapter interface-realtime-adapter.php Live event broadcasting to connected clients

Built-in Adapters (Free)

Adapter Class Type Active When
Fulltext_Search (Jetonomy\Search\) Search Always (MySQL FULLTEXT - built-in)
WP_Mail_Adapter Email Always (uses wp_mail())
WP_Roles_Adapter Membership Always (WP role-based membership fallback)
Polling_Adapter Realtime Always (long-polling fallback via /updates endpoint)
MemberPress_Adapter Membership MemberPress plugin is active
PMPro_Adapter Membership Paid Memberships Pro is active

Pro Adapters (Jetonomy Pro)

Adapter Class Type Active When
WooCommerce_Adapter Membership WooCommerce Memberships is active
RCP_Adapter Membership Restrict Content Pro is active
LearnDash_Adapter Membership LearnDash is active (4.x and 5.x)
Tutor_Adapter Membership Tutor LMS is active
LifterLMS_Adapter Membership LifterLMS is active
Sensei_Adapter Membership Sensei LMS is active
MasterStudy_Adapter Membership MasterStudy LMS is active

Pro registers these via Adapter_Registry::register_membership() at plugins_loaded priority 20.


Adapter_Registry API

// Register adapters.
\Jetonomy\Adapters\Adapter_Registry::register_search( 'my-search', $adapter );
\Jetonomy\Adapters\Adapter_Registry::register_email( 'my-mailer', $adapter );
\Jetonomy\Adapters\Adapter_Registry::register_membership( 'my-membership', $adapter );
\Jetonomy\Adapters\Adapter_Registry::register_realtime( 'my-pusher', $adapter );

// Retrieve the active adapter (first registered adapter where is_active() returns true).
$search     = \Jetonomy\Adapters\Adapter_Registry::get_search();
$email      = \Jetonomy\Adapters\Adapter_Registry::get_email();
$membership = \Jetonomy\Adapters\Adapter_Registry::get_membership();
$realtime   = \Jetonomy\Adapters\Adapter_Registry::get_realtime();

// Retrieve a specific adapter by ID.
$mp = \Jetonomy\Adapters\Adapter_Registry::get_membership( 'memberpress' );

// List all registered membership adapters.
$all = \Jetonomy\Adapters\Adapter_Registry::get_all_membership();

The Registry returns null when no active adapter is found for a type - always null-check before calling methods.

Registration timing: Register your adapters at plugins_loaded. Use priority 9 if you want your adapter to override a built-in default (e.g. replacing built-in search). Use priority 15 for additive adapters that do not need to override defaults (e.g. adding a new membership source):

add_action( 'plugins_loaded', function() {
    if ( ! class_exists( '\Jetonomy\Adapters\Adapter_Registry' ) ) {
        return; // Jetonomy not active.
    }
    \Jetonomy\Adapters\Adapter_Registry::register_search(
        'meilisearch',
        new My_Plugin\Meilisearch_Adapter()
    );
}, 15 );

Search Adapter Interface

namespace Jetonomy\Adapters;

interface Search_Adapter {
    /** Return true when this adapter is ready to handle queries. */
    public function is_active(): bool;

    /** Index a document. Called when a post or reply is created or updated. */
    public function index( string $object_type, int $object_id, array $data ): void;

    /**
     * Execute a search query.
     *
     * @param string   $query      The search string.
     * @param string   $type       'post', 'reply', or 'space'.
     * @param int|null $space_id   Optional space filter.
     * @param int      $limit      Max results (default 20).
     * @param int      $offset     Pagination offset.
     * @return array               Array of result objects with at least: id, title (posts/spaces) or content (replies).
     */
    public function search( string $query, string $type, ?int $space_id, int $limit, int $offset ): array;

    /** Remove a document from the index. Called when a post or reply is deleted. */
    public function delete( string $object_type, int $object_id ): void;
}

Example: Custom Elasticsearch Adapter

<?php
namespace My_Plugin;

use Jetonomy\Adapters\Search_Adapter;

class Elasticsearch_Adapter implements Search_Adapter {

    private \Elasticsearch\Client $client;

    public function __construct() {
        // Build your Elasticsearch client here.
        $this->client = \Elasticsearch\ClientBuilder::create()
            ->setHosts( [ get_option( 'my_plugin_es_host', 'localhost:9200' ) ] )
            ->build();
    }

    public function is_active(): bool {
        // Only activate when the Elasticsearch host option is set and the client connects.
        $host = get_option( 'my_plugin_es_host' );
        return ! empty( $host ) && $this->client->ping();
    }

    public function index( string $object_type, int $object_id, array $data ): void {
        $this->client->index( [
            'index' => 'jetonomy_' . $object_type,
            'id'    => $object_id,
            'body'  => $data,
        ] );
    }

    public function search( string $query, string $type, ?int $space_id, int $limit, int $offset ): array {
        $must = [
            [ 'multi_match' => [ 'query' => $query, 'fields' => [ 'title^3', 'content' ] ] ],
        ];

        if ( $space_id ) {
            $must[] = [ 'term' => [ 'space_id' => $space_id ] ];
        }

        $params = [
            'index' => 'jetonomy_' . $type,
            'body'  => [
                'query' => [ 'bool' => [ 'must' => $must ] ],
                'from'  => $offset,
                'size'  => $limit,
            ],
        ];

        $raw = $this->client->search( $params );

        // Map Elasticsearch hits to the flat object array Jetonomy expects.
        return array_map(
            fn( $hit ) => (object) array_merge( $hit['_source'], [ 'id' => (int) $hit['_id'] ] ),
            $raw['hits']['hits'] ?? []
        );
    }

    public function delete( string $object_type, int $object_id ): void {
        $this->client->delete( [
            'index' => 'jetonomy_' . $object_type,
            'id'    => $object_id,
        ] );
    }
}

Register it:

add_action( 'plugins_loaded', function() {
    if ( ! class_exists( '\Jetonomy\Adapters\Adapter_Registry' ) ) {
        return;
    }
    \Jetonomy\Adapters\Adapter_Registry::register_search(
        'elasticsearch',
        new My_Plugin\Elasticsearch_Adapter()
    );
}, 15 );

Jetonomy will call is_active() on every registered search adapter and use the first one that returns true. Because the built-in Fulltext_Search adapter always returns true, register your custom adapter before the defaults are initialized - or override it by making sure your adapter is registered first.

The built-in defaults are initialized at plugins_loaded priority 10 via Adapter_Registry::init_defaults(). Registering at priority 15 means your adapter is added after the defaults, but since get_search() iterates in insertion order, you need to register at priority 9 if you want your adapter to take precedence:

add_action( 'plugins_loaded', function() {
    // Priority 9 - runs before Jetonomy's init_defaults() at priority 10.
    \Jetonomy\Adapters\Adapter_Registry::register_search( 'elasticsearch', new My_Plugin\Elasticsearch_Adapter() );
}, 9 );

Email Adapter Interface

namespace Jetonomy\Adapters;

interface Email_Adapter {
    public function is_active(): bool;

    /**
     * Send a single transactional email.
     *
     * @param string   $to            Recipient email address.
     * @param string   $subject       Email subject line.
     * @param string   $html          HTML body.
     * @param string   $plain         Plain-text fallback.
     * @param string[] $extra_headers Additional mail headers.
     * @return bool True on success.
     */
    public function send( string $to, string $subject, string $html, string $plain, array $extra_headers = [] ): bool;

    /**
     * Send a batch of emails.
     *
     * @param array $messages Array of ['to', 'subject', 'html', 'plain'] arrays.
     * @return array          Results array indexed by recipient.
     */
    public function send_batch( array $messages ): array;

    /** Register any hooks needed (e.g. intercepting wp_mail for logging). */
    public function register_hooks(): void;
}

Example: Postmark Adapter

class Postmark_Adapter implements \Jetonomy\Adapters\Email_Adapter {

    public function is_active(): bool {
        return ! empty( get_option( 'my_plugin_postmark_token' ) );
    }

    public function send( string $to, string $subject, string $html, string $plain, array $extra_headers = [] ): bool {
        $token = get_option( 'my_plugin_postmark_token' );
        $from  = get_option( 'admin_email' );

        $response = wp_remote_post( 'https://api.postmarkapp.com/email', [
            'headers' => [
                'Accept'                  => 'application/json',
                'Content-Type'            => 'application/json',
                'X-Postmark-Server-Token' => $token,
            ],
            'body' => wp_json_encode( [
                'From'     => $from,
                'To'       => $to,
                'Subject'  => $subject,
                'HtmlBody' => $html,
                'TextBody' => $plain,
            ] ),
        ] );

        return ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response );
    }

    public function send_batch( array $messages ): array {
        $results = [];
        foreach ( $messages as $msg ) {
            $results[ $msg['to'] ] = $this->send( $msg['to'], $msg['subject'], $msg['html'], $msg['plain'] );
        }
        return $results;
    }

    public function register_hooks(): void {
        // Optional - intercept wp_mail if you want to route ALL site email through Postmark.
    }
}

Membership Adapter Interface

namespace Jetonomy\Adapters;

interface Membership_Adapter {
    public function is_active(): bool;

    /** Return all active membership level IDs for a user. */
    public function get_user_levels( int $user_id ): array;

    /** Check whether a user has a specific membership level. */
    public function user_has_level( int $user_id, string $level_id ): bool;

    /** Return all available membership levels as ['id' => ..., 'name' => ...] objects. */
    public function get_all_levels(): array;

    /** Return the human-readable label for a level ID. */
    public function get_level_label( string $level_id ): string;

    /** Register any hooks needed for lifecycle events (e.g. activation/deactivation). */
    public function register_hooks(): void;
}

The register_hooks() method is where you fire jetonomy_membership_activated and jetonomy_membership_deactivated - see 02-hooks-reference.md.

Example: Custom Membership Adapter

class My_Membership_Adapter implements \Jetonomy\Adapters\Membership_Adapter {

    public function is_active(): bool {
        return defined( 'MY_MEMBERSHIP_VERSION' );
    }

    public function get_user_levels( int $user_id ): array {
        return (array) get_user_meta( $user_id, 'my_membership_levels', true );
    }

    public function user_has_level( int $user_id, string $level_id ): bool {
        return in_array( $level_id, $this->get_user_levels( $user_id ), true );
    }

    public function get_all_levels(): array {
        return my_membership_get_all_plans(); // Your own function.
    }

    public function get_level_label( string $level_id ): string {
        return my_membership_get_plan_name( $level_id ) ?? $level_id;
    }

    public function register_hooks(): void {
        // Fire Jetonomy's membership hooks so space access is updated automatically.
        add_action( 'my_membership_activated', function( int $user_id, string $plan_id ) {
            do_action( 'jetonomy_membership_activated', $user_id, $plan_id );
        }, 10, 2 );

        add_action( 'my_membership_cancelled', function( int $user_id, string $plan_id ) {
            do_action( 'jetonomy_membership_deactivated', $user_id, $plan_id );
        }, 10, 2 );
    }
}

Realtime Adapter Interface

namespace Jetonomy\Adapters;

interface Realtime_Adapter {
    public function is_active(): bool;

    /**
     * Broadcast an event to all clients subscribed to a channel.
     *
     * @param string $channel Channel name (e.g. 'post.42', 'space.7').
     * @param string $event   Event type (e.g. 'new-reply', 'post-updated').
     * @param array  $data    Event payload.
     */
    public function publish( string $channel, string $event, array $data ): void;

    /**
     * Return configuration passed to the frontend JavaScript client.
     * Keys depend on your provider (e.g. 'key', 'cluster' for Pusher).
     *
     * @return array
     */
    public function get_client_config(): array;
}

The built-in Polling_Adapter uses the /updates REST endpoint as a fallback. If you register a WebSocket-based adapter (Pusher, Ably, Soketi), the frontend Interactivity API store picks up the client config from get_client_config() and switches to push-based updates automatically.

Example: Pusher Adapter

class Pusher_Adapter implements \Jetonomy\Adapters\Realtime_Adapter {

    private \Pusher\Pusher $pusher;

    public function __construct() {
        $this->pusher = new \Pusher\Pusher(
            get_option( 'my_plugin_pusher_key' ),
            get_option( 'my_plugin_pusher_secret' ),
            get_option( 'my_plugin_pusher_app_id' ),
            [ 'cluster' => get_option( 'my_plugin_pusher_cluster', 'mt1' ), 'useTLS' => true ]
        );
    }

    public function is_active(): bool {
        return class_exists( '\Pusher\Pusher' )
            && ! empty( get_option( 'my_plugin_pusher_key' ) );
    }

    public function publish( string $channel, string $event, array $data ): void {
        $this->pusher->trigger( $channel, $event, $data );
    }

    public function get_client_config(): array {
        return [
            'key'     => get_option( 'my_plugin_pusher_key' ),
            'cluster' => get_option( 'my_plugin_pusher_cluster', 'mt1' ),
        ];
    }
}

Connecting Adapters to Jetonomy Events

Adapters do not self-wire - you need to connect them to Jetonomy's lifecycle hooks to trigger indexing, emailing, or broadcasting at the right time.

Search: Index content on create/update

add_action( 'jetonomy_after_create_post', function( int $post_id, int $space_id ) {
    $search = \Jetonomy\Adapters\Adapter_Registry::get_search();
    if ( ! $search ) return;

    $post = \Jetonomy\Models\Post::find( $post_id );
    if ( $post ) {
        $search->index( 'post', $post_id, [
            'title'      => $post->title,
            'content'    => wp_strip_all_tags( $post->content ),
            'space_id'   => $post->space_id,
            'author_id'  => $post->author_id,
            'created_at' => $post->created_at,
        ] );
    }
}, 10, 2 );

add_action( 'jetonomy_post_deleted', function( int $post_id ) {
    $search = \Jetonomy\Adapters\Adapter_Registry::get_search();
    $search?->delete( 'post', $post_id );
} );

Realtime: Broadcast new replies

add_action( 'jetonomy_after_create_reply', function( int $reply_id, int $post_id ) {
    $rt = \Jetonomy\Adapters\Adapter_Registry::get_realtime();
    if ( ! $rt ) return;

    $reply = \Jetonomy\Models\Reply::find( $reply_id );
    if ( $reply ) {
        $rt->publish( 'post.' . $post_id, 'new-reply', [
            'reply_id'   => $reply_id,
            'author_id'  => $reply->author_id,
            'created_at' => $reply->created_at,
        ] );
    }
}, 10, 2 );

Summary: Registration Cheat Sheet

add_action( 'plugins_loaded', function() {
    if ( ! class_exists( '\Jetonomy\Adapters\Adapter_Registry' ) ) {
        return;
    }

    // Search - replace built-in MySQL FULLTEXT.
    \Jetonomy\Adapters\Adapter_Registry::register_search(
        'meilisearch',
        new My_Plugin\Meilisearch_Adapter()
    );

    // Email - replace wp_mail for notification emails.
    \Jetonomy\Adapters\Adapter_Registry::register_email(
        'postmark',
        new My_Plugin\Postmark_Adapter()
    );

    // Membership - add a custom membership source.
    $adapter = new My_Plugin\My_Membership_Adapter();
    $adapter->register_hooks();
    \Jetonomy\Adapters\Adapter_Registry::register_membership( 'my-membership', $adapter );

    // Realtime - replace long-polling with WebSockets.
    \Jetonomy\Adapters\Adapter_Registry::register_realtime(
        'pusher',
        new My_Plugin\Pusher_Adapter()
    );
}, 9 ); // Priority 9 ensures search adapter runs before built-in defaults at priority 10.

What's Next?

Developer reference for the FluentCommunity coexistence integration shipped in Jetonomy 1.3.8. This page is for plugin/theme developers extending or debugging the integration. End users should start with the FluentCommunity integration guide.

What You Will Learn

  • Where the integration lives and when it loads
  • Which WordPress options persist its state
  • Which FluentCommunity and Jetonomy hooks it consumes
  • Which helpers you can reuse from your own code
  • How loop protection, identity keying, and stale-pair handling work

File Layout

The entire integration lives in one class:

includes/integrations/class-fluent-community.php

Loaded from includes/class-jetonomy.php only when FluentCommunity is active:

if ( class_exists( '\\FluentCommunity\\App\\App' ) ) {
    require_once JETONOMY_DIR . 'includes/integrations/class-fluent-community.php';
    new Integrations\Fluent_Community();
}

No autoload entry beyond this gate. Deactivate FluentCommunity and nothing from this file is parsed or instantiated.

Persisted State

Two WordPress options hold the entire integration footprint. No custom tables, no post meta, no user meta.

Option Type Default Purpose
jetonomy_fc_space_pairs array {fc_space_id: jt_space_id} [] Space pairing map. One row holds every pair.
jetonomy_fc_tab_label string Discussions Label used on the FC tab, the Jetonomy sidebar card, and the FC profile Discussions block.
jetonomy_fc_sync_members '1' / '0' '1' Toggle for bidirectional member sync.
jetonomy_fc_broadcast '1' / '0' '1' Toggle for topic broadcast to the paired FC feed.

All four options are removed on uninstall by Jetonomy's standard jetonomy_* option sweep in uninstall.php.

Reading the pair map

$pairs = get_option( 'jetonomy_fc_space_pairs', array() );
if ( is_array( $pairs ) ) {
    foreach ( $pairs as $fc_space_id => $jt_space_id ) {
        // Use as needed. Both IDs are integers when the map is valid.
    }
}

Reverse lookup

To find the Jetonomy space paired with a given FC space (or the reverse), walk the map:

$fc_id_for_jt = function ( int $jt_id ) {
    $pairs = get_option( 'jetonomy_fc_space_pairs', array() );
    if ( ! is_array( $pairs ) ) {
        return 0;
    }
    foreach ( $pairs as $fc => $jt ) {
        if ( (int) $jt === $jt_id ) {
            return (int) $fc;
        }
    }
    return 0;
};

FluentCommunity Hooks Consumed

Hook Type Surface
get_avatar_url (core WP) filter Unifies the avatar across both sides by reading wp_fcom_xprofile.avatar keyed on user_id.
fluent_community/space_header_links filter Appends the Discussions tab to the FC space header.
fluent_community/activity/after_contents_user filter Renders the Discussions block on the FC profile.
fluent_community/space/joined action Triggers member sync from FC to Jetonomy.
fluent_community/comment_added action Triggers the comment-to-reply bridge on broadcast feed comments.

All FC hook names were verified against FluentCommunity 2.3.0.

Jetonomy Hooks Consumed

Hook Type Surface
jetonomy_admin_settings_tabs action Registers the FluentCommunity settings tab.
jetonomy_admin_settings_tab_content action Renders the tab body when active.
jetonomy_sidebar_after_about action Renders the "Also on {community}" sidebar card on paired Jetonomy spaces.
jetonomy_profile_after_stats action Renders the cross-link to the member's FluentCommunity profile.
jetonomy_user_joined_space action Triggers member sync from Jetonomy to FC.
jetonomy_after_create_post action Triggers the broadcast of a new topic to the paired FC feed.

Identity Keying

Everything joins on user_id, never on username. user_id is the primary key in both wp_fcom_xprofile and wp_jt_user_profiles, so the integration stays correct no matter how usernames diverge across the two plugins.

The integration ships a static helper to map user_id to the FC username for URL construction:

// Private in the integration class. Shape reproduced here for reference.
// Returns null when the user has no FC xprofile row.
fc_username_for_user( int $user_id ): ?string

If you need this elsewhere, query wp_fcom_xprofile.username by user_id directly. The result is request-scoped cached inside the integration class.

Community Name Helper

fc_site_title(): string reads FC's configured site_title from the fluent_community_settings option and falls back to the WP site name, then to the translated string Community. This is what drives the dynamic button and card labels. Reuse the same option key if you are rendering your own cross-links:

$settings = get_option( 'fluent_community_settings', array() );
$title    = is_array( $settings ) && ! empty( $settings['site_title'] )
    ? (string) $settings['site_title']
    : get_bloginfo( 'name' );

Loop Protection

Member sync and broadcast use a static $syncing flag so a join or post on one side never triggers a boomerang write back:

// Pseudocode matching the real implementation.
if ( self::$syncing ) {
    return;
}
self::$syncing = true;
try {
    // Write to the other side.
} finally {
    self::$syncing = false;
}

For the comment-to-reply bridge, only comments on broadcast feed posts round-trip. Broadcast feed rows are tagged with a meta marker when Jetonomy creates them, and the bridge listens only for comments on rows carrying that marker. Native FC feed posts never create Jetonomy replies.

Stale Pair Handling

At render time, every surface re-resolves the paired space ID. If the referenced FC or Jetonomy space no longer exists (deleted, trashed, or the pair option references an invalid ID), the tab or card silently disappears and no admin cleanup is required. The integration never fatals on a stale pair.

Add-Only Semantics

Member sync is deliberately add-only:

  • Joins propagate in both directions.
  • Leaves do not propagate. Removing yourself from one side never yanks you out of the other.
  • Role changes do not propagate. Each plugin manages its own role structure.

If you build on top of this integration and need leave-sync or role-sync behaviour, do it in a separate extension with an explicit per-pair toggle and a visible admin warning. The defaults stay add-only to avoid accidental bulk removals.

Privacy Guard

Topics marked as private (is_private = 1) are never broadcast to FluentCommunity. The FC feed audience can be broader than the private-topic scope, so the guard prevents leaking private content to a wider audience. If you add your own broadcast surfaces on top, apply the same guard:

if ( ! empty( $post->is_private ) ) {
    return; // Skip broadcast.
}

REST Architecture Note

FluentCommunity ships as a Vue SPA consuming its REST API. All the PHP filters the integration uses run inside REST response preparation, and their output flows through to the SPA render automatically. No Jetonomy-side REST additions or JS injection are needed for v1 of the integration.

Verified against live FC endpoints at build time:

Endpoint Filter that lands output in SPA
GET /fluent-community/v2/spaces/{slug}/by-slug fluent_community/space_header_links populates header_links
GET /fluent-community/v2/profile/{username} fluent_community/profile_view_data populates profile_navs (not used in v1)
GET /fluent-community/v2/activities?... fluent_community/activity/after_contents_user appends to the user activity view

Extending

Want to build on top? Two clean extension points:

  • Listen for the broadcast. The integration calls fluent_community/feed/created after creating the FC feed row, so your code can react to broadcasts with your own handler.
  • Replace a surface. Because every render hook exits early when its pair resolves to nothing, you can remove the integration's handler from jetonomy_sidebar_after_about (priority 10) and register your own without code-level conflicts.

Destructive extensions (leave-sync, role-sync, privacy mirroring) belong in a Pro extension with explicit per-pair toggles and a backfill tool, not as drop-in replacements for the free integration.

Developer reference for the BuddyPress coexistence integration. This page is for plugin/theme developers extending or debugging the integration. End users should start with the BuddyPress integration guide.

What You Will Learn

  • Where the integration lives and when it loads
  • What state persists (group meta + options) and where
  • Which BuddyPress and Jetonomy hooks the integration consumes
  • How the activity broadcast and comment-to-reply bridge work, and how to extend them
  • How loop protection, identity keying, and stale-pair handling are implemented

File Layout

The integration lives in a single class:

includes/integrations/class-buddypress.php

Loaded from includes/class-jetonomy.php only when the BuddyPress Groups component is active:

if ( function_exists( 'bp_is_active' ) && bp_is_active( 'groups' ) ) {
    require_once JETONOMY_DIR . 'includes/integrations/class-buddypress.php';
    new Integrations\BuddyPress();
}

The broadcast and comment-bridge methods additionally gate themselves on bp_is_active( 'activity' ) at runtime, so a BP install with Groups but not Activity stays fatal-free.

Persisted State

Where Key Type Purpose
BP group meta jetonomy_space_id int Points a group at its paired Jetonomy space. One value per group.
WP option jetonomy_bp_broadcast '1' / '0' Toggle for JT topic → BP group activity broadcast. Defaults on.
WP option jetonomy_bp_comment_bridge '1' / '0' Toggle for BP activity comment → JT reply bridge. Defaults on.
BP activity meta jetonomy_post_id int Tags a broadcast activity row with its originating Jetonomy post ID. The comment bridge reads this to decide which activity comments should round-trip as JT replies.

Class constants

BuddyPress::META_KEY              = 'jetonomy_space_id';
BuddyPress::OPT_BROADCAST         = 'jetonomy_bp_broadcast';
BuddyPress::OPT_COMMENT_BRIDGE    = 'jetonomy_bp_comment_bridge';
BuddyPress::ACTIVITY_META_POST    = 'jetonomy_post_id';
BuddyPress::ACTIVITY_TYPE         = 'jetonomy_topic';

Reading the pair

$space_id = (int) groups_get_groupmeta( $group_id, \Jetonomy\Integrations\BuddyPress::META_KEY, true );

Reverse lookup

$group_id = \Jetonomy\Integrations\BuddyPress::find_group_by_space( $space_id );

This runs a single meta-keyed query, no get_posts() loop.

BuddyPress Hooks Consumed

Group lifecycle

Hook Handler Note
groups_created_group on_group_created + save_group_forum_settings_on_create Reads the jt_bp_forum_action form field ('create', 'link_{id}', 'none'). Only creates a new space when explicitly requested.
groups_delete_group on_group_deleted Unlinks the space. Space itself is preserved.
groups_details_updated on_group_updated Syncs name, description, and visibility (public/private/hidden) to the paired space.

Member sync

Hook Handler Direction
groups_join_group on_member_join BP → JT
groups_leave_group on_member_leave BP → JT
groups_remove_member on_member_leave BP → JT
groups_ban_member on_member_leave BP → JT
groups_unban_member on_member_join BP → JT
groups_promote_member on_member_promote BP → JT (admin/mod)
groups_demote_member on_member_demote BP → JT (back to member)

Activity

Hook Handler Note
bp_register_activity_actions register_activity_type Registers the jetonomy_topic activity type with bp_activity_set_action so BP renders it alongside native types.
bp_activity_comment_posted on_bp_activity_comment_posted Runs the comment-to-reply bridge when the parent activity carries the broadcast meta marker.
bp_activity_allowed_tags filter_broadcast_allowed_tags Adds <br> and <p> to BP's kses allowlist so broadcast paragraphs survive save AND display. Both tags carry no attributes, so no XSS surface.

Render

Hook Handler
bp_setup_nav (priority 20) register_group_forum_tab + register_profile_forum_tab
groups_custom_group_fields_editable render_group_forum_settings (the dropdown in Create + Manage > Details)
groups_group_details_edited save_group_forum_settings
bp_after_group_details_creation_step render_group_forum_settings (creation step)

Jetonomy Hooks Consumed

Hook Handler Surface
jetonomy_before_content render_back_to_group_banner Renders the "← Group Name" link at the top of paired space / topic pages.
jetonomy_sidebar_about_after_meta render_sidebar_group_link Renders the small tag in the sidebar About card linking back to the BP group.
jetonomy_user_joined_space not directly hooked; member sync is BP → JT only (BP is the source of truth for group membership). n/a
jetonomy_after_create_post on_jt_post_created_for_bp Triggers the broadcast to the paired BP group activity stream.

Activity Broadcast Flow

On jetonomy_after_create_post:

  1. If broadcast is disabled or no pair exists for the space, return.
  2. If the post is private (is_private), return.
  3. If the BP Activity component is not active, return.
  4. Build the activity body: excerpt converted to <p> paragraphs with block-level tag boundaries preserved, plus a trailing "Shared from the forum · View discussion" attribution line.
  5. Call bp_activity_add with component=groups, type=jetonomy_topic, item_id=$group_id, secondary_item_id=$post_id, and hide_sitewide set when the group is not public.
  6. Store the post ID in activity meta: bp_activity_update_meta( $activity_id, 'jetonomy_post_id', $post_id ).

The bp_activity_allowed_tags filter that whitelists <br> and <p> is attached globally while broadcast is enabled. BP runs kses both on save and on display, so a per-call toggle would strip the tags when the activity is rendered later.

Comment-to-Reply Bridge Flow

On bp_activity_comment_posted( $comment_id, $r, $activity ):

  1. If the loop-guard flag is set, return. Prevents boomerang writes.
  2. If bp_activity_get_meta( $activity->id, 'jetonomy_post_id' ) is empty, the parent activity is not one of ours, return.
  3. Load the Jetonomy post; if it is not published, return (the broadcast survives, but we do not create replies against draft/trashed topics).
  4. Build the reply content: wp_kses_post on the comment HTML for display, wp_strip_all_tags for the plain version.
  5. Create the reply via Reply::create with the same author as the BP commenter.

Edits and deletes on BP do NOT propagate. The JT thread is the durable record.

Loop Protection

A shared static $syncing flag stops a write on one side from triggering a boomerang write back. Every member-sync, broadcast, and bridge method flips it for the duration of the write:

self::$syncing = true;
// do the write that might fire hooks we listen to
self::$syncing = false;

Both the group-lifecycle handlers (on_group_created, on_group_updated) and the member-sync handlers read self::$syncing at entry.

Identity Keying

Everything joins on user_id. BP member profiles and Jetonomy user profiles share the same WP user ID, so username divergence is not a problem.

Stale Pair Handling

Every render hook resolves the paired entity lazily. If the paired space no longer exists when the forum tab is about to render, the tab callback returns early without emitting markup. The same pattern applies to the sidebar link and back-banner.

Extending

Three clean extension points:

  • Disable member-leave propagation. Remove the groups_leave_group, groups_remove_member, and groups_ban_member actions from the integration at init + 30 or later if you want the add-only semantics the FluentCommunity integration uses.
  • Custom activity rendering. Filter bp_activity_action_before_save or add a filter on bp_get_activity_action to override how jetonomy_topic rows render without touching the integration.
  • Custom permission gate on forum tab. Filter bp_is_user_in_group (or call groups_is_user_member directly) inside your own hook handler on register_group_forum_tab (priority < 20) to restrict the Forum tab to certain roles.

Destructive or privacy-affecting extensions (forcing role sync one-way, propagating flags cross-surface, cascading deletes) belong in a Pro extension with explicit per-pair toggles, not as drop-in replacements for the free integration.

Jetonomy 1.4.1 introduced the Public / Private community toggle documented in Access Control Settings. On the code side that toggle is enforced through two pieces:

  1. A small helper class - Jetonomy\Visibility - that every front-end template and REST permission callback can call to decide "is this caller allowed to see community content right now?"
  2. A shell script - bin/access-matrix-check.sh - that walks every public REST route as every role in both modes and asserts the responses match the documented contract.

This page covers both.

Namespace: Jetonomy\ **Source:** includes/class-visibility.php, bin/access-matrix-check.sh


Why the helper exists

Before 1.4.1 the public/private check was sprinkled across templates, the template loader, and individual REST controllers. Each call site had its own "is the community private and the user a guest?" expression, and any new endpoint had to remember to repeat the pattern. The fastest way to ship a leak was to forget the check.

Jetonomy\Visibility consolidates that into one read of jetonomy_settings.guest_read. The front-end template loader and every public-read REST endpoint route through the same helper, so they answer the same question with the same logic. New endpoints opt into the gate with a single line.

The helper deliberately does not look at per-resource visibility (private spaces, blocked users, restricted posts). Those remain the responsibility of individual controllers - Visibility only answers the global "can this caller see ANY community content right now?" question.


API Reference

Visibility::can_view_community()

Returns true if the current request should be allowed to see community content, false otherwise. In public mode this is always true. In private mode it requires the caller to be authenticated.

Returns: bool

Example - gating a custom template fragment:

add_action( 'jetonomy_sidebar_before', function () {
    if ( ! \Jetonomy\Visibility::can_view_community() ) {
        return;
    }
    echo do_shortcode( '[my_member_only_widget]' );
} );

The check is global, not per-resource - you do not need to pass a user ID or a post ID. Per-resource visibility (private spaces, restricted access rules) is enforced separately inside the relevant controllers.


Visibility::get_mode()

Returns the active visibility mode as a string. Useful when you want to render a different UI in private mode (e.g. a "Members only" badge in the site header) without duplicating the option read.

Returns: string - 'public' or 'private'

Example - tagging the body class:

add_filter( 'body_class', function ( array $classes ): array {
    $classes[] = 'jt-mode-' . \Jetonomy\Visibility::get_mode();
    return $classes;
} );

The function defaults to 'public' on a fresh install - an unset, null, or true value of guest_read all resolve to public. Only an explicit false flips the community to private.


Visibility::rest_check()

Designed to be used as a REST permission_callback. Returns true when the caller may proceed, or a 401 WP_Error with code community_private when the community is in private mode and the caller is not logged in.

Returns: true|\WP_Error

Example - protecting your own REST route:

register_rest_route( 'my-plugin/v1', '/community-events', [
    'methods'             => 'GET',
    'callback'            => 'my_plugin_list_events',
    'permission_callback' => [ '\Jetonomy\Visibility', 'rest_check' ],
] );

Example - chaining with an existing capability check:

'permission_callback' => static function ( $request ) {
    $vis = \Jetonomy\Visibility::rest_check( $request );
    if ( is_wp_error( $vis ) ) {
        return $vis;
    }
    return current_user_can( 'read' );
},

Anonymous calls in private mode return:

{
    "code": "community_private",
    "message": "This community is private. Please log in to view content.",
    "data": { "status": 401 }
}

Authenticated calls fall through to your route's own permission logic.

Note: /auth/* and /admin/* endpoints intentionally do not route through this helper. Locking /auth/* would lock users out forever, and admin endpoints have their own capability gates. Apply Visibility::rest_check to public-read endpoints only.


The Access Matrix Runner

bin/access-matrix-check.sh is the regression safety net that proves the helper actually works. It walks a representative subset of every Jetonomy REST route as every role, makes the real HTTP call, and asserts the response code matches the documented expectation.

What it covers

  • 78 checks across the public REST surface
  • 6 roles - anonymous, subscriber, author, editor (space-admin), moderator, administrator
  • Both modes - the runner can flip jetonomy_settings.guest_read to private for the duration of the run and restore it on exit (even if a check fails midway)

In public mode the runner asserts that anonymous reads return 200. In private mode it asserts the same anonymous reads return 401 from the central Visibility::rest_check gate. Logged-in calls are mode-independent and stay unchanged in either mode.

Running it locally

# From the plugin root:
bin/access-matrix-check.sh                # public mode (default)
bin/access-matrix-check.sh --mode=private # private community gate
bin/access-matrix-check.sh --quiet        # only show failures

Sample output:

PASS  GET   /spaces             [anon]       expected=200  got=200
PASS  GET   /spaces             [subscriber] expected=200  got=200
PASS  POST  /posts/123/vote     [anon]       expected=401  got=401
FAIL  GET   /posts/recent       [anon]       expected=401  got=200  <- regression

Summary: 77 passed, 1 failed (78 total)

A regression is any row where the actual response code does not match the documented expectation. Exit code is 0 on a clean run, 1 if any row fails.

Release gating

bin/build-release.sh invokes the access matrix runner before producing the release zip. If any row regresses, the build aborts and no zip is produced - the gate exists so we never ship a release that quietly opens up a previously-locked endpoint or quietly locks a previously-open one.

Run the matrix locally before every commit that touches permission_callback wiring, the Visibility helper, or REST route registration.


What's Next?

Jetonomy ships a small JavaScript toolkit that replaces native window.confirm, window.alert, and window.prompt with branded, accessible modal dialogs. The toolkit lives in assets/js/jetonomy-modals.js and is enqueued on every community page and inside wp-admin.

Globals: window.jetonomyConfirm, window.jetonomyAlert, window.jetonomyPrompt Localisation contract: window.jetonomyModalsI18n (added in 1.4.2) Source: assets/js/jetonomy-modals.js


Why the toolkit exists

Native browser dialogs are ugly, untranslatable, blocking, not styleable, and inconsistent across browsers - some hide them, some show "Prevent this page from creating additional dialogs" checkboxes, mobile Safari renders them at the bottom of the viewport. They also bypass any focus-management or screen-reader contract that the rest of the community UI follows.

The toolkit gives you:

  • A consistent visual language that matches Jetonomy's --jt-* token system
  • Promise-based async / await syntax instead of blocking calls
  • Keyboard support - ESC dismisses, Enter confirms (or commits a single-line prompt)
  • Backdrop click to cancel
  • Focus trapping while the modal is open, focus restoration on close
  • Built-in translatable labels via window.jetonomyModalsI18n

Every custom JS that ships in the Jetonomy ecosystem should use these globals instead of native dialogs. That includes Pro extensions, theme bridge plugins, and third-party integrations that hook into community pages.


API Reference

jetonomyConfirm( message, opts? )

Asks the user a yes/no question. Resolves true when the user confirms, false when they cancel, press ESC, or click the backdrop.

Parameters

Parameter Type Description
message string The body text shown inside the modal
opts.title string? Heading text. Omit for a body-only modal
opts.confirmLabel string? Confirm button label. Defaults to the localised "Confirm" string
opts.cancelLabel string? Cancel button label. Defaults to the localised "Cancel" string
opts.danger boolean? When true, the confirm button uses the danger style (red). Use for destructive actions

Returns: Promise<boolean>

Example:

const proceed = await window.jetonomyConfirm(
    'Delete this post? This cannot be undone.',
    {
        title:        'Delete post',
        confirmLabel: 'Delete',
        cancelLabel:  'Keep it',
        danger:       true,
    }
);

if ( ! proceed ) {
    return;
}
await myPlugin.deletePost( postId );

jetonomyAlert( message, opts? )

Shows a message and waits for the user to dismiss it. Resolves true once the user clicks the confirm button, presses Enter, presses ESC, or clicks the backdrop.

Parameters

Parameter Type Description
message string The body text shown inside the modal
opts.title string? Heading text
opts.confirmLabel string? Confirm button label. Defaults to "OK"

Returns: Promise<true>

Example:

await window.jetonomyAlert(
    'Your changes have been saved.',
    { title: 'Saved' }
);

// Code here runs after the user dismisses the dialog.
location.reload();

jetonomyPrompt( message, opts? )

Asks the user for a string input. Resolves the submitted string on submit, or null on cancel / ESC / backdrop click.

Parameters

Parameter Type Description
message string The body text shown above the input field
opts.title string? Heading text
opts.placeholder string? Placeholder text shown inside the empty input
opts.defaultValue string? Pre-filled value. The input is auto-selected on open so the user can overwrite or accept it
opts.multiline boolean? When true, renders a <textarea> instead of an <input type="text">
opts.confirmLabel string? Submit button label. Defaults to "Submit"
opts.cancelLabel string? Cancel button label. Defaults to "Cancel"

Returns: Promise<string|null> - the input value on submit, null on cancel

Example:

const reason = await window.jetonomyPrompt(
    'Tell us why you are reporting this reply.',
    {
        title:        'Report reply',
        placeholder:  'Optional context for the moderator team',
        multiline:    true,
        confirmLabel: 'Submit report',
    }
);

if ( reason === null ) {
    return; // User cancelled.
}

await myPlugin.reportReply( replyId, reason );

Single-line prompts commit on Enter; multiline prompts require an explicit button click (Enter inserts a newline as expected).


End-to-end Example

A custom moderator action that confirms, prompts for a note, then alerts on success - all without touching a native dialog:

async function quarantinePost( postId ) {
    const proceed = await window.jetonomyConfirm(
        'Quarantine this post? It will be hidden from public view until reviewed.',
        {
            title:        'Quarantine post',
            confirmLabel: 'Quarantine',
            danger:       true,
        }
    );

    if ( ! proceed ) {
        return;
    }

    const note = await window.jetonomyPrompt(
        'Add a moderator note (optional).',
        {
            title:        'Moderator note',
            placeholder:  'e.g. flagged for review by 3 members',
            multiline:    true,
            confirmLabel: 'Save note',
        }
    );

    if ( note === null ) {
        return; // User backed out at the note stage.
    }

    await fetch( `/wp-json/my-plugin/v1/quarantine/${ postId }`, {
        method:  'POST',
        headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': wpApiSettings.nonce },
        body:    JSON.stringify( { note } ),
    } );

    await window.jetonomyAlert(
        'Post quarantined. Moderators have been notified.',
        { title: 'Done' }
    );
}

The user gets three branded, translatable, keyboard-friendly modals in sequence instead of three jarring native dialogs.


Localisation: window.jetonomyModalsI18n

Jetonomy localises four default button labels onto window.jetonomyModalsI18n via wp_localize_script. The script is registered on both the front-end and inside wp-admin, so the global is reliably available wherever the toolkit JS is loaded.

Shape:

window.jetonomyModalsI18n = {
    cancel:  'Cancel',  // Default cancel button label (jetonomyConfirm, jetonomyPrompt)
    confirm: 'Confirm', // Default confirm button label (jetonomyConfirm)
    submit:  'Submit', // Default submit button label (jetonomyPrompt)
    ok:      'OK',      // Default OK button label (jetonomyAlert)
};

The values are translated through WordPress's standard i18n pipeline - if your site loads a jetonomy translation file, the strings arrive pre-translated and the modals adopt the active locale automatically.

How third-party callers should use it

The global is provided so you can read the localised strings when you need additional context inside your own UI (for example, to label a custom action that mirrors one of the toolkit buttons):

const cancelLabel = ( window.jetonomyModalsI18n && window.jetonomyModalsI18n.cancel ) || 'Cancel';

myDropdown.appendChild( makeMenuItem( cancelLabel, onCancel ) );

Do not override the global. Overrides are not supported and may be reset on the next page load. If you want a different label on a single modal, pass it via the per-call opts.confirmLabel / opts.cancelLabel instead:

// Correct - per-call override
await window.jetonomyConfirm( 'Publish?', { confirmLabel: 'Publish now', cancelLabel: 'Keep as draft' } );

// Wrong - mutating the global
window.jetonomyModalsI18n.confirm = 'Publish now'; // do not do this

This keeps the global predictable for every other piece of code that reads from it.


What's Next?

Something unclear? Open a support ticket →

Buy Jetonomy