close
Wbcom Designs Jetonomy Docs
Back to product Buy Now

Getting Started

Install, configure, and launch your community in minutes.

Everything Jetonomy does, on one page. This is the fastest way to see whether Jetonomy fits your community - it groups every feature by what it actually does for you, links straight to the deep-dive guide for each area, and marks which features are free and which are Jetonomy Pro.

The Jetonomy community home page showing categories, spaces, and recent activity

How to Use This Page

Each section below is a job your community needs done - publishing discussions, organizing them, keeping them clean, recognizing contributors, and so on. Read the benefit, scan what is included, then follow the links to the full guide for anything you want to set up.

Features marked Pro require Jetonomy Pro. Everything else ships in the free plugin.

Content & Discussions

This is the heart of Jetonomy: a fast, modern composer and a reply system built for threads with hundreds of contributions. Members write clear topics, the best answers float to the top, and nobody loses a half-finished post.

What is included:

  • Topics - the primary unit of discussion, with a Markdown composer, inline image upload, tags, similar-topic detection, and topic prefixes. See Creating Topics.
  • Replies and threading - threaded conversations, multiple sort orders, and efficient loading for long threads. See Replies & Threading.
  • Voting - upvote and downvote on posts and replies so the most useful content rises naturally. See Voting.
  • Accepted answers - mark the reply that solved a question in Q&A spaces. See Replies & Threading.
  • Drafts and scheduling - save work in progress and publish now or at a future date. See Drafts & Scheduling.

Pro adds:

  • Polls - attach a single or multi-select poll to any topic for decisions and feedback. See Polls.
  • Reactions - emoji reactions on posts and replies so members can respond without writing a full reply. See Reactions.

Spaces & Organization

Spaces are how you keep a large community navigable. Group them into categories, pick the right space type for each purpose, and control exactly who can join. The right structure makes your community feel purpose-built instead of generic.

What is included:

  • Categories and spaces - top-level categories that group focused discussion spaces. See Creating Spaces.
  • Five space types - Forum, Q&A, Ideas, Show & Tell, and Social Feed, each changing how posts and replies behave. See Space Types.
  • Membership and join policies - public, private, or hidden visibility paired with open, approval-required, or invite-only join rules. See Membership & Join Policies.
  • Invites - invite-link landing pages so the right people get in without manual setup. See Membership & Join Policies.

Pro adds:

  • Custom Fields - structured fields on member profiles so you collect exactly the information your community needs. See Custom Fields.

Moderation & Trust

Jetonomy keeps quality high without a moderator watching every post. Trust grows automatically with good participation, members flag problems, and one queue handles everything that needs human review.

What is included:

  • Trust levels 0 to 5 - members earn higher trust and unlock capabilities automatically as they contribute. See Trust Levels.
  • Flagging and reporting - any logged-in member can report content that breaks your rules. See Flagging & Reporting.
  • Moderation queue - a single dashboard for posts awaiting approval, flagged content, and spam-caught items. See Moderation Queue.
  • Anti-spam - multiple silent layers of spam protection that real members never notice. See Anti-Spam.
  • Banning - remove and block disruptive members from your community. See Trust Levels.
  • Pinning and announcements - keep important topics at the top of a space. See Topic Management.

Pro adds:

  • Advanced Moderation - rules that catch bad content automatically before it ever appears. See Advanced Moderation.
  • Site Announcements - feature one post at the top of every space across the whole community. See Site Announcements.

Members & Gamification

Recognition keeps people coming back. Every member gets a public profile, reputation built from real contributions, and a place on the leaderboard worth competing for.

What is included:

  • Member profiles - a public page showing who a member is and what they have contributed. See User Profiles.
  • Reputation - a contribution score that powers trust levels and rankings. See User Profiles.
  • Leaderboard - public recognition for your top contributors. See Leaderboard.

Pro adds:

  • Custom Badges - design your own badges and the conditions members earn them by. See Custom Badges.

Search & Discovery

Members can only engage with content they can find. Full-text search and cross-space tags make every discussion discoverable the moment it is published.

What is included:

  • Full-text search - fast search across topics and replies with filters. See Search & Filters.
  • Tags - connect related discussions across every space from a single tag page. See Tags.

Pro adds:

  • SEO Pro - per-space meta titles, Open Graph images, Twitter Cards, schema markup, and sitemap rules. See SEO Pro.

Notifications & Messaging

Jetonomy brings members back when something relevant happens. Every event has an in-app notification and an email, and Pro adds direct messaging plus the channels members actually check.

What is included:

  • In-app notifications - real-time alerts for replies, mentions, votes, and more. See Notifications.
  • Email notifications - an email for every event type, with per-member control over what is delivered. See Email Settings.

Pro adds:

  • Private Messaging - one-on-one and small-group direct messages without leaving your community. See Private Messaging.
  • Email Digest - a curated daily or weekly summary of community activity. See Email Digest.
  • Web Push - browser push notifications that reach members even after they close your site. See Web Push.
  • Reply by Email - members reply to discussions straight from their email client. See Reply by Email.

Admin & Branding

Run the whole community from a clean WordPress admin. Every setting has a home, every action is logged, and Pro lets you present the community as entirely your own.

What is included:

  • Settings - configure general behavior, permissions, email, appearance, SEO, anti-spam, and access control. See General Settings.
  • Admin pages - manage spaces, categories, content, and users from the dashboard. See General Settings.
  • Activity log - a read-only, filterable audit trail of every important event. See Activity Log.

Pro adds:

  • White Label - remove all Jetonomy branding and present the community as your own product. See White Label.

Analytics & Integrations

See what your community is doing and connect it to the tools you already run. Jetonomy integrates with the major membership, commerce, learning, and community plugins so access follows enrollment automatically.

Pro adds:

  • Analytics - a single dashboard for growth, engagement, and your most active members. See Analytics.
  • Webhooks - send community events to Slack, Zapier, your CRM, or any custom pipeline. See Webhooks.

Integrations included:

  • MemberPress - gate spaces by membership level. See MemberPress.
  • Paid Memberships Pro - gate spaces by PMPro level. See Paid Memberships Pro.
  • WooCommerce - unlock spaces on product purchase or active subscription. See WooCommerce.
  • LearnDash - pair course and group enrollment with discussion spaces. See LearnDash.
  • Tutor LMS - pair course enrollment with discussion spaces. See Tutor LMS.
  • BuddyPress - make groups and forum spaces feel like one membership. See BuddyPress.
  • FluentCommunity - run the social feed and the forum as one product. See FluentCommunity.

Developer Platform

Jetonomy is built to be extended. Everything members can do is reachable through a documented REST API, hooks, WP-CLI, blocks, and shortcodes - so you can automate, embed, and customize without touching core.

What is included:

  • REST API - 48+ free endpoints under jetonomy/v1, 90+ with Pro active. See REST API.
  • Hooks - 58 free hooks plus 9 in Pro for actions and filters. See Hooks Reference.
  • WP-CLI - 14 free command roots and 15 in Pro for headless automation. See WP-CLI.
  • Blocks and shortcodes - eight Gutenberg blocks, four widgets, and seven shortcodes to embed community content anywhere. See Shortcodes, Widgets & Blocks.

Pro adds:

  • AI - bring large language models into spam detection, auto-moderation, reply suggestions, and thread summaries, with self-hosted options. See AI.

What's Next?

Ready to set it up? Start with installation, then run the setup wizard to go live.

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

The Jetonomy admin dashboard is your first stop after logging in to wp-admin. It shows a live snapshot of community health and gives you one-click access to the most common management tasks.

What You Will Learn

  • What each stat card measures
  • How to use the Quick Actions panel
  • What the Recent Activity feed shows
  • Where to find System Info

Go to Jetonomy → Dashboard to access this screen. It is also the landing page when you click Jetonomy in the wp-admin sidebar.

Setup Wizard Notice

On a fresh install where the setup wizard has not been completed, a blue notice appears at the top of the dashboard asking you to run the wizard. The wizard creates your first space and seeds optional demo content. Once you complete it, the notice disappears and does not return.

Stat Cards

Six cards show current counts for the most important community metrics. Counts update in real time - refreshing the page fetches the latest values.

Card What It Counts
Total Posts All published posts across every space
Total Replies All published replies across every post
Active Spaces Spaces with status "active"
Registered Users Total WordPress users with at least one Jetonomy profile row
Pending Flags Flags in the moderation queue that have not been resolved
Posts Today Posts created in the current calendar day (UTC)

The Pending Flags card turns orange when the count is greater than zero - a visual signal to visit the moderation queue.

Recent Activity Feed

The Recent Activity table on the left side of the dashboard shows the most recent member actions logged in the activity system. Columns:

Column Description
User Display name of the member who took the action
Action What they did (colored dot indicates category: green = create, blue = vote, orange = moderation)
Object The post, reply, space, or user the action involved, with a link to the relevant admin page
When Relative time (e.g. "3 minutes ago")

If no activity has been logged yet, a placeholder message appears. The feed is read-only - use it for monitoring, not for taking action on specific items.

Quick Actions Panel

The right sidebar has a Quick Actions card with three buttons:

Button What It Does
Create Space Opens the new-space form at Jetonomy → Spaces → Add New
View Community Opens the community home in a new browser tab
Flush Rules Triggers flush_rewrite_rules() to rebuild WordPress permalink rules

Use Flush Rules any time your community URLs return 404s after changing the base slug, activating a new plugin, or running a migration.

Demo Data Notice

If you used the setup wizard's demo-data option, a yellow card labeled Demo Data Active appears below Quick Actions. Click Remove All Demo Data to delete all demo posts, replies, spaces, and the related setup option.

This card only appears while jetonomy_demo_data option is set. It disappears permanently once you click Remove.

System Info

A small table at the bottom of the sidebar shows:

Row Value
Plugin Version Current Jetonomy version constant
DB Version The database schema version currently applied
PHP Version Server PHP version
WordPress Version WordPress core version
Base URL The configured community base slug (e.g. /community/)

Use this table when filing a support request or diagnosing compatibility issues. Copy the values rather than describing them from memory.

Pro Analytics Widget (Jetonomy Pro)

When Jetonomy Pro is active and the Analytics extension is enabled, Pro adds an analytics mini-widget to the sidebar showing recent engagement trends. A full analytics dashboard is available at Jetonomy → Analytics.

When Pro is not active, a small card in the sidebar describes what the Analytics extension adds and links to the Jetonomy Pro store page.

What's Next?

Configure the General settings to set your community URL and access controls.

General Settings →

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 →

Related Pro Features

  • Custom Fields - add structured fields to posts and profiles, configurable per space.

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 →

Related Pro Features

These Pro extensions add to the discussion experience:

  • Polls - attach a single or multi-select poll to any topic.
  • Reactions - emoji reactions on posts and replies.

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 person who asked the question (the topic author) decides which reply is the accepted answer. This signals to everyone else that the question has been solved and surfaces the winning reply at the top of the thread.

Marking an Answer as Accepted

As the asker, you are the one who confirms which reply actually solved your problem:

  1. Open your own question. You must be the post author (space moderators and admins can also accept on your behalf, see below).
  2. Find the reply that best answers your question.
  3. Click the Accept button, the checkmark icon shown below the reply, on each reply in a Q&A space.
  4. The reply is immediately pinned to the top of the reply list, above all other replies and regardless of the current sort order.

What changes the moment you accept:

  • The reply gets a green Accepted tag and the whole thread is marked as resolved.
  • An "Accepted answer" callout box appears at the top of the topic, so anyone landing on the question sees the solution first without scrolling.
  • The reply's author receives a notification and a +15 reputation bonus (you do not earn reputation for accepting your own reply).

Changing or Removing the Accepted Answer

The acceptance is never locked in. As the asker you stay in control:

  • Switch answers: Click Accept on a different reply. The previous reply automatically loses its accepted status, so there is only ever one accepted answer at a time.
  • Unaccept entirely: On the currently accepted reply, click the Unaccept button (the x-circle icon). This clears the accepted answer, returns the question to the unresolved state, removes the callout, and revokes the reputation that was awarded when it was accepted, so trust scores stay honest.

Tip: Space moderators (and admins) can also accept or unaccept answers on any Q&A topic in their space, useful for resolving questions on behalf of an asker who never came back to mark a solution.

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 which sort tab is selected (Latest, Popular, or Unanswered). Use pinning for space rules, important reference threads, or a "start here" topic. Pinning here affects only this space - to feature a post across the whole community, see Community Announcements (Pro, admins only).

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

The topic moves to the top of the listing immediately and shows a green Pinned badge - on both the listing row and the topic's own header - so members can see at a glance that it is pinned.

To unpin, open the same menu and select Unpin.

Each space allows up to 3 pinned topics by default. Once the limit is reached, pinning another topic returns "You can pin up to 3 topics in a space. Unpin one first." The cap keeps the top of the space scarce and meaningful; developers can change it with the jetonomy_max_space_pins filter.

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 link previews turn a pasted URL into a rich card
  • How members add images, and which capability gates uploads
  • 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.

Link Previews

Paste a URL on its own line in a post or reply and Jetonomy automatically fetches the page's metadata and renders a rich preview card beneath it, the same style of card you see when you share a link on LinkedIn or Twitter. Members get context (title, description, thumbnail, site name) without leaving the thread.

How It Works

When a link sits alone on its own line (not inline inside a sentence), Jetonomy calls GET /jetonomy/v1/link-preview?url=... and renders the result as a card. The card shows:

  • Thumbnail image (when the page provides one)
  • Page title
  • Short description
  • Site name and favicon

The preview endpoint pulls metadata from Open Graph and Twitter Card tags, so it covers almost any site with social tags (news outlets, blogs, GitHub, LinkedIn, Instagram, Facebook, and more). For the major sanctioned oEmbed hosts (YouTube, Vimeo, TikTok, Spotify, SoundCloud, Reddit, and the rest of the WordPress oEmbed registry) it returns a true rich embed instead of a static card.

Behavior and Limits

  • Up to 3 preview cards render per post or reply, so a message full of links does not turn into a wall of cards.
  • Results are cached on the server (roughly 12 hours for a successful fetch, a few minutes for a failed one), so a 200-reply thread does not refetch the same link 200 times.
  • Mentions (@name) and tag links never turn into preview cards, only standalone web URLs do.

Developer Filters

The link preview pipeline is fully filterable:

Filter Purpose
jetonomy_link_preview_providers Add or reorder host-specific providers (e.g. a custom intranet URL rewriter) ahead of the defaults
jetonomy_link_preview_data Final mutation of the preview data right before it is cached and returned
jetonomy_link_preview_cache_ttl Override the cache lifetime, in seconds
jetonomy_link_preview_user_agent Override the user agent used when fetching the page (some corporate firewalls only allow specific agents)

Media Uploads

The composer lets members add images to posts and replies in three ways:

  • Toolbar button - click the image button in the composer toolbar to open a file picker.
  • Drag and drop - drag an image straight from your desktop onto the editor.
  • Paste - paste a screenshot or copied image directly from your clipboard.

Uploads go to POST /jetonomy/v1/media, which stores the file as a standard WordPress attachment and returns its URL so the image drops into the editor inline. Every uploaded image is given alt text automatically (derived from the file name) for accessibility and SEO, and members can supply their own alt text.

Who Can Upload

Uploading is gated by capability. A member can upload media if they have any one of:

  • upload_files - the standard WordPress capability (Author and above)
  • jetonomy_upload_media - the Jetonomy role-map capability, granted to the Contributor role and automatically granted at Trust Level 1
  • jetonomy_create_posts or jetonomy_create_replies - anyone who can post or reply

In practice this means most contributing members can attach images, and members earn the upload capability automatically as they reach Trust Level 1 even if their WordPress role would not otherwise allow it.

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 →

Related Pro Features

  • SEO Pro - per-space meta, schema.org markup, OG/Twitter cards, and sitemaps so search engines surface your community.

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.

Admin Tag Management

Members create tags on the fly as they post, but admins get a dedicated management screen for the whole global tag namespace at Jetonomy → Tags in the WordPress admin. Managing tags here requires the jetonomy_manage_settings capability.

The page lists every tag with its post count and gives you these controls:

Action What it does
Add New Tag Create a tag directly from the admin (name, optional slug) without having to post first
Edit Rename a tag or change its slug. The change applies everywhere the tag is used
Delete Remove a single tag. You can force-delete a tag even when posts are still attached to it
Bulk Delete Tick multiple tags, choose Delete from the bulk-action dropdown, and click Apply to clear several at once

The list also has a search box and a per-page picker, and it paginates so it stays fast even on communities with thousands of tags.

Use this screen to clean up duplicate or misspelled tags (for example merging "stripe" and "Stripe" by deleting the stray one), retire tags that are no longer relevant, or pre-create the canonical tags you want members to reuse. Because the namespace is global, an edit or delete here affects every space at once.

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
  • How to ban a member when content-level actions are not enough

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.

Banning Members

Approve, Mark as Spam, and Trash all act on individual pieces of content. Banning acts on the person. When a member is not just posting one bad item but is repeatedly disruptive, you ban the account so they cannot keep posting. Banning requires the jetonomy_moderate capability, the same capability that gates the moderation queue itself.

You can ban a member from the moderation tools and from the Jetonomy → Users admin page, which lists every community member with a Ban / Unban control on each row.

Ban Types

Jetonomy supports three levels of restriction so the response fits the situation:

Type Effect
Global ban The member is blocked from posting anywhere in the community
Space ban The member is blocked from a single space but can still participate elsewhere
Silence The member can still read but cannot post or reply

Duration

A ban can be permanent or time-limited. Pick a preset of 1 day, 7 days, or 30 days, or set a custom number of days. Time-limited bans lift themselves automatically when they expire, so you do not have to remember to unban a member after a cooling-off period. You can also record a reason with the ban for your own records.

Lifting a Ban

To remove a ban before it expires, click Unban on the member's row. The restriction is cleared and the member can participate again immediately.

Tip: Reach for a ban only when content-level actions are not enough. For a single bad post, Trash or Mark as Spam is the right tool. For a member who keeps coming back, a global ban (permanent or timed) stops the behavior at the source. The full member list and per-member history live on the Jetonomy → Users admin page.

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 →

Related Pro Features

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, filter, and delete notifications on the full notifications page
  • How the verification reminder email nudges members who never confirmed their address
  • 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 25 per page. Notifications older than 90 days are automatically cleaned up from the database by a background cron job.

Filter Tabs

The page has a row of filter tabs across the top so members can focus on one kind of update at a time. Each tab shows a count badge of how many notifications match it, so you can see at a glance where the activity is.

Tab What it shows
All Every notification you have received
Unread Only notifications you have not opened yet
Mentions Notifications where someone @-mentioned you
Replies Replies to your posts and to your replies
Votes Upvotes and downvotes on your posts and replies
Badges Badges you have earned (only shown when Jetonomy Pro is active)

Switching tabs reloads the list filtered to that type and keeps your place. The Badges tab appears only on communities running Jetonomy Pro, since badges are a Pro feature.

Deleting Notifications

Members can clear out notifications they no longer need, not just mark them read.

  • Delete one: Open the ... actions menu on any notification row and choose Delete. The row is removed immediately.
  • Bulk delete: Tick the checkbox on one or more rows. A toolbar appears at the top of the list showing how many you have selected, with Mark read and Delete buttons that apply to every selected row at once. A Select all on page checkbox selects the whole visible page in one tick.

Deleting is permanent for that member's own inbox. It does not affect anyone else's notifications or the underlying post or reply.

Note: Per-notification delete and the bulk-delete toolbar were added in 1.4.3.

The Verification Reminder Email

When a community requires email verification at signup (Jetonomy → Settings → Email), some members register but never click the verification link in their welcome email. Jetonomy sends those members a single follow-up reminder email nudging them to finish verifying, so they do not get stuck unable to participate.

  • The reminder is sent once per member. It will never email the same person twice.
  • It goes out a configurable number of hours after registration. Set the delay in jetonomy_settings.verification_reminder_hours. Setting it to 0 disables the reminder entirely.
  • It respects each member's email opt-out, so anyone who has opted out of community email is skipped.
  • It uses the same branded email template as your other Jetonomy emails, so the subject and body can be customised under Settings → Email → Email Templates.
  • The reminder only runs while email verification is switched on. Turn verification off and there is nothing to remind about.

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 →

Related Pro Features

Pro adds more ways to reach and message members:

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.

Bookmarks

Lists the topics this member has bookmarked, at /community/u/their-username/bookmarks/. Like Drafts, this tab appears only on the member's own profile. Bookmarks are private to the member who saved them, so no one else (including administrators viewing the public profile) sees this tab. It gives members a quick personal reading list of posts they wanted to come back to. See Bookmarks & Following for how members save and manage bookmarks.

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 →

Related Pro Features

  • Custom Badges - award badges automatically or by hand to recognize members.

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.

Activate your license, switch on the extensions you need, and understand which capabilities gate each Pro feature.

PRO - This section covers Jetonomy Pro.

What You Will Learn

  • How to activate your Jetonomy Pro license
  • How to enable and disable individual Pro extensions
  • Which license tier unlocks which extensions
  • Which WordPress capabilities gate each Pro feature

Activate Your License

Jetonomy Pro is a peer plugin to free Jetonomy. Install and activate free Jetonomy first, then install Jetonomy Pro - Pro shows an admin notice and stays dormant if the free plugin is not active.

  1. Go to Jetonomy → License in your WordPress admin (the jetonomy-license page).
  2. Paste the license key from your purchase receipt.
  3. Click Activate. Jetonomy validates the key against the store and shows your tier and expiry.

Your license drives automatic updates and tier checks. If a license is missing or expired, extensions still boot - but features that require a higher tier than your license carries are blocked at the gate.

Note: Free and Pro always ship the same x.y.z version. Keep both updated together so the contracts between them stay in sync.

Enable Extensions

Jetonomy Pro ships fifteen extensions. None of them do anything until you switch them on.

  1. Go to Jetonomy → Extensions (the jetonomy-extensions page).
  2. Each extension shows a card with its name, description, and a toggle.
  3. Click Enable on the extensions you want. The toggle persists the choice to the jetonomy_pro_extensions option (an array of extension IDs).
  4. Enabling an extension runs any one-time setup it needs (for example, creating its database tables) and registers its hooks, REST routes, and admin screens immediately.

Disable an extension at any time from the same page. Disabling stops the feature and unregisters its hooks; it does not delete the data the extension already stored.

License Tiers

Tier Who it suits
Starter A single community getting started with Pro features
Growth A growing community that needs the full extension set across more sites
Agency Builders running Pro across many client sites
Lifetime One-time purchase with ongoing updates

All fifteen extensions are available on the paid tiers. Higher tiers raise the activation limits (number of sites) and support level rather than locking individual features behind a paywall. The exact site limits per tier are listed on your account page and on the pricing page.

Capabilities

Pro registers five capabilities that gate its features. Each maps to the WordPress roles shown below by default; you can reassign them with any role-editor plugin if your community uses custom roles.

Capability Default roles What it gates
jetonomy_manage_settings Administrator Access to the Pro settings pages (License, Extensions, integration and extension settings tabs)
jetonomy_manage_spaces Administrator Per-space Pro admin controls - SEO Pro metadata, custom-field tabs, and creating site-wide announcements
jetonomy_moderate Editor, Administrator The actions Advanced Moderation rules can take on matched content
jetonomy_vote Subscriber, Contributor, Author, Editor, Administrator Casting Reactions and voting in Polls
jetonomy_view_analytics Administrator Viewing the community Analytics dashboard

Note: Most Pro REST write routes are additionally gated by manage_options for admin operations, and by trust level for member operations (for example, Private Messaging and Poll creation require Trust Level 1 or higher). See the per-feature pages and the REST API reference for the exact permission on each route.

What's Next?

Start with the engagement extensions members notice first.

Emoji Reactions →

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 registers these endpoints under jetonomy/v1:

Method Endpoint Description
GET /posts/{id}/reactions Get the reactions and counts for a post
POST /posts/{id}/reactions Toggle your reaction on a post (add, replace, or remove)
GET /replies/{id}/reactions Get the reactions and counts for a reply
POST /replies/{id}/reactions Toggle your reaction on a reply

{id} is the numeric ID of the post or reply. A single POST toggles your reaction: sending a new emoji replaces your previous one, and sending your current emoji again clears it.

Example - toggle a reaction:

POST /wp-json/jetonomy/v1/posts/42/reactions
{
  "emoji": "rocket"
}

Reading reactions is open to any visitor; toggling a reaction requires the jetonomy_vote capability (logged-in members by default). See the REST API reference for full payloads.

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.

Group Conversations

A conversation is either direct (1:1) or a group with multiple participants. Add several recipients in the composer to start a group thread, and every participant sees the same shared history.

Group threads keep a complete record. When a member leaves a group, their past messages stay in place attributed to them with a "left the conversation" note - Jetonomy does not delete the messages, so the thread always reads coherently. Creating a direct message to someone you already have a 1:1 thread with reuses that existing thread instead of starting a duplicate.

Archive, Leave, and Mute

Each of these is a per-member setting - your choice never changes the conversation for the other participants.

  • Mute - silences notifications for a conversation while keeping it in your inbox. New messages still arrive; you just are not pinged. Unmute from the same menu.
  • Archive - hides a conversation from your main inbox to keep it tidy. Archived conversations stay fully intact and reappear (or can be reopened) when there is new activity; nothing is deleted.
  • Leave - removes you from a group conversation. You stop receiving its messages, but the thread and your past messages remain for everyone still in it. Leaving applies to group conversations.

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, filterable)
POST /conversations Start a new direct or group conversation
GET /conversations/{id} Get conversation details and participant list
PATCH /conversations/{id} Update your settings on the conversation (mute)
GET /conversations/{id}/messages List messages (cursor-based pagination)
POST /conversations/{id}/messages Send a message
GET /conversations/unread-count Your total unread conversation count (30s cache)
POST /conversations/{id}/mute Mute or unmute the conversation for you
POST /conversations/{id}/archive Archive or unarchive the conversation for you
POST /conversations/{id}/leave Leave a group conversation
POST /conversations/{id}/block Block or unblock the other participant (direct only)

All endpoints require authentication, and member operations on a conversation require you to be a participant. Starting a conversation and sending messages require Trust Level 1 or higher (admins and moderators bypass this). See the REST API reference for full payloads.

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.

Multi-Select Voting

A poll is either single choice or multiple choice, set when you build it:

  • Single choice records exactly one option per member. Voting for a different option moves the vote.
  • Multiple choice lets each member pick several options at once. Each option toggles independently - a member can hold any number of selections, and clicking a chosen option a second time clears just that one.

Because of this, the total vote count on a multiple-choice poll can be higher than the number of voters - one member can contribute several votes.

Polls Admin

Enabling the extension adds a Polls submenu under the Jetonomy admin menu (jetonomy-pro-polls). It lists every poll in your community with its post, type, total votes, and close state, so you can review and close polls without opening each topic.

REST API

Polls registers these endpoints under jetonomy/v1:

Method Endpoint Description
POST /polls Create a poll on a post
GET /polls/{id} Get a poll's options and live vote counts
POST /polls/{id}/vote Cast a vote - pass option_id for single choice or option_ids (array) for multiple choice
DELETE /polls/{id}/vote Remove your vote(s) from the poll
PATCH /polls/{id} Update a poll (for example, change the close date)

Creating a poll and voting require the member to be logged in; poll creation additionally requires the right to post in the target space. See the REST API reference for full request and response payloads.

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
POST /custom-field-values Set a single field value on a target object (user or post)

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.

Setting one value at a time: if you want to set a single field on a single object rather than sending the whole custom_fields object, POST /custom-field-values with the field key, the target object type and ID, and the value. This is the lightweight path used by the edit-profile form and is handy for partial updates from custom tooling.

How fields surface in output: once a field is configured, its value is embedded automatically in the relevant REST responses - the custom_fields object on /users and /posts (and their list endpoints) - and rendered on the matching frontend surface (the member profile for profile fields, the post for post fields), subject to the field's visibility setting. You do not register a separate read endpoint per field; the value rides along with the object it belongs to.

See the REST API reference for full payloads.

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.

REST API

Custom Badges registers these endpoints under jetonomy/v1:

Method Endpoint Description
GET /badges List all defined badges and their settings
POST /badges Create a badge
PATCH /badges/{id} Update a badge
DELETE /badges/{id} Delete a badge
POST /users/{id}/badges Award a badge to a member

Badges can be created, edited, and awarded entirely through REST, so you can automate awards from your own tooling or grant a badge as part of an external workflow. Listing badges is open to any logged-in member; creating, editing, deleting, and awarding require manage_options. See the REST API reference for full payloads.

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.

REST API

Advanced Moderation manages rules under jetonomy/v1:

Method Endpoint Description
GET /moderation/rules List all moderation rules
POST /moderation/rules Create a rule
PATCH /moderation/rules/{id} Update a rule
DELETE /moderation/rules/{id} Delete a rule
GET /moderation/rules/{id}/stats Get a rule's trigger statistics

All routes require manage_options. See the REST API reference for full payloads.

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.

REST API

Email Digest exposes endpoints under jetonomy/v1:

Method Endpoint Description
GET /users/me/digest-preferences Read the current member's digest frequency and section choices
PATCH /users/me/digest-preferences Update the current member's digest preferences
POST /admin/digest/test Send a test digest (admin)
GET /admin/digest/stats Read digest send statistics (admin)

Members manage their own preferences; the /admin/* routes require manage_options. See the REST API reference for full payloads.

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.

REST API

Web Push registers these endpoints under jetonomy/v1:

Method Endpoint Description
POST /push/subscribe Register the current member's browser push subscription
DELETE /push/subscribe Remove the current member's push subscription
GET /push/vapid-key Fetch the public VAPID key the browser needs to subscribe
GET /push/service-worker.js Serve the service worker script

Subscribe and unsubscribe require the member to be logged in; the VAPID key and service worker are served for the page to register push. See the REST API reference for full payloads.

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.

Branding Settings Reference

White Label stores its configuration in the jetonomy_pro_white_label option. The settings worth calling out:

Setting What it does
header_logo_url The logo actually displayed in the community header. This is the image members see at the top of every community page.
logo_url The fallback / Open Graph logo. Used for social share cards (OG image) and anywhere a logo is needed but no header logo is set. Keep this set even if you customize the header separately.
accent_color A global accent colour override. Recolours Pro-rebranded surfaces - including email accents - in one place so your brand colour is consistent across the community and its emails.
custom_css Freeform CSS injected on every community page. It loads after Jetonomy's own stylesheet, so your rules always win - use it for brand colour and spacing tweaks without touching theme files.
sidebar_auth_card_html Custom HTML for the community sidebar's sign-in / call-to-action card. When set, it replaces Jetonomy's default auth card with your own markup; leave it empty to keep the default card.
footer_text Replaces the "Powered by Jetonomy" footer text.
email_logo_url Logo shown at the top of notification and digest emails.

Tip: header_logo_url and logo_url are deliberately separate. Set header_logo_url for the on-page logo and logo_url for the share-card / fallback image - they are often different sizes and aspect ratios.

REST API

White Label exposes its settings under jetonomy/v1:

Method Endpoint Description
GET /settings/white-label Read the current white-label settings
PATCH /settings/white-label Save white-label settings

Both routes require manage_options. See the REST API reference for full payloads.

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.

Feature an important post at the top of every space across your whole community.

PRO - This feature requires Jetonomy Pro.

What You Will Learn

  • What a community announcement is, and how it differs from pinning a topic in a space
  • Who is allowed to create announcements
  • How to pin a post to the community and where it appears
  • How many announcements you can have at once
  • How to remove an announcement

Announcements vs Space Pinning

Jetonomy has two separate "pin" tools for two different jobs:

Space pin ("Pin") Community announcement ("Pin to community")
Scope Top of one space only Top of every space across the community
Who can use it Space moderators and admins Administrators only
Badge shown green Pinned green Announcement
Where to find it The topic's ... menu A Pin to community button on the topic

Use a space pin for a "start here" thread inside one forum. Use a community announcement for something everyone should see no matter which space they are browsing - a maintenance notice, a major release, or community-wide rules. Space pinning is covered in Topic Management.

Who Can Create Announcements

Community announcements are administrator-only. The control is gated by the manage_options and jetonomy_manage_spaces capabilities, and jetonomy_manage_spaces is granted only to the Administrator role by default. Moderators who can pin topics within their own space cannot create site-wide announcements - that power stays with admins, because an announcement affects every space.

Pinning a Post to the Community

  1. Open the post you want to feature.
  2. In the action bar below the post, click Pin to community.

The post is immediately featured across the community. The button changes to Unpin from community, and an Announcement badge appears on the post.

Where Announcements Appear

A community announcement is shown in two places:

  • At the top of every space's listing - above that space's own topics, with an Announcement badge, so members see it wherever they browse.
  • On the post's own header - with the same Announcement badge.

A post can be both space-pinned and a community announcement at the same time; in that case it shows both the Pinned and Announcement badges, which is expected.

The Announcement Limit

You can have up to 5 community announcements at once. When the limit is reached, pinning another post returns "You can only pin 5 announcements at a time. Unpin one first." Keeping the set small protects the value of the slot - if everything is an announcement, nothing is.

Removing an Announcement

Open the post and click Unpin from community in the action bar. The post returns to its normal position everywhere and the Announcement badge is removed. This does not delete or unpin the post within its own space - if it was also space-pinned, that pin remains.

Related

  • Topic Management - space-level pinning, closing, moving, and merging topics
  • Developer Guide: REST API - the site-announcements endpoints. Pinning and unpinning require the jetonomy_manage_spaces capability (Administrator by default); listing the current pins is public.

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.

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.

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.

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.

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.

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.

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.

Require Email Verification

Setting: require_email_verification Default: off Location: General tab → Access section

When this is on, new members who register through Jetonomy's Login block receive a confirmation email immediately after sign-up. They cannot log in until they click the verification link in that email. Existing members are not affected - this setting applies only to accounts created after you enable it.

A follow-up reminder is sent automatically if the member has not confirmed within the configured window.

Setting: verification_reminder_hours Default: 24 Location: Stored in jetonomy_settings['verification_reminder_hours']

This controls how many hours after registration the reminder email is sent. The reminder runs on an hourly WP-Cron schedule (hook: jetonomy_verification_reminder). The email template can be customized on the Settings → Email screen under the "Verification reminder" row.

Note: If you use a third-party registration flow (WooCommerce, Restrict Content Pro, LearnDash) instead of Jetonomy's Login block, those plugins handle their own email verification. This setting only applies to registrations that go through the Jetonomy Login block.

Rebuild Counters

Location: Dashboard → Quick Actions, or via WP-CLI / REST API

Jetonomy stores denormalized counters (reply counts per post, post counts per space, member counts per space, vote scores) for performance. These counters are updated in real time on every write, but they can drift from true values after bulk database edits, server failures, or plugin imports.

Use Rebuild Counters to recalculate all denormalized values from the canonical tables.

WP-CLI:

wp --path="/path/to/wordpress" jetonomy recount
wp --path="/path/to/wordpress" jetonomy recount --type=posts   # posts only
wp --path="/path/to/wordpress" jetonomy recount --type=spaces  # spaces only
wp --path="/path/to/wordpress" jetonomy recount --type=votes   # votes only
wp --path="/path/to/wordpress" jetonomy recount --type=users   # user profile counters only

REST API (site admin only):

POST /jetonomy/v1/admin/recount
{ "type": "all" }

Valid type values: all, posts, spaces, votes, users. Omit or pass all to rebuild everything. The response reports how many rows were updated per step. On a large community (100,000+ posts), a full recount may take 10-60 seconds - run it during low-traffic hours.

Tip: After running an import from bbPress, wpForo, or Asgaros, always run a full recount. The importers insert rows directly and skip the counter-update logic.

Admin Bar Shortcut

All logged-in members see a Community menu in the WordPress admin bar. This menu links to the community home, your notification inbox, and your profile.

Members who have the jetonomy_manage_spaces or manage_options capability also see an admin sub-section with direct links to: Manage Spaces, Add New Space, Categories, Moderation Queue, Posts and Replies, and Settings.

The admin bar menu appears on both the public site and inside wp-admin, so you can jump from any page in the dashboard directly into the community and back.

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 →

Related Pro Features

  • White Label - replace Jetonomy branding with your own.
  • Analytics - community health and engagement dashboard.
  • Webhooks - send community events to external services.

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

Reputation Points

Setting: jetonomy_settings['reputation_points'] Location: Permissions tab → Reputation Points section

Every community action awards or deducts a fixed number of reputation points. You can override any default value on this screen. Positive numbers reward the action; negative numbers penalize it.

Action key Label Default
post_upvoted Post upvoted +10
reply_upvoted Reply upvoted +5
post_downvoted Post downvoted -2
reply_downvoted Reply downvoted -2
reply_accepted Reply accepted as answer +15
idea_planned Idea moved to Planned/Shipped +20
flag_validated Flag confirmed (reporter) +5
post_reported Post reported -10
post_removed Post removed by moderator -20

The Default column shows each value before any override is saved. You can restore a row to its default by deleting your override and saving again.

Lookup order at runtime: saved admin override in jetonomy_settings['reputation_points'] → hardcoded defaults above → 0. This means a site with no overrides still scores correctly out of the box.

Developer hook: jetonomy_reputation_points_for

add_filter( 'jetonomy_reputation_points_for', function( int $points, string $action ): int {
    // Double the reward for replies accepted as answers.
    if ( 'reply_accepted' === $action ) {
        return $points * 2;
    }
    return $points;
}, 10, 2 );

The filter runs after the admin override and hardcoded fallback resolve, so $points already reflects any per-site override. Return an int.

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.

The Users admin screen gives moderators and administrators a full view of every community member - with one-click access to ban, silence, or change trust level without leaving wp-admin.

What You Will Learn

  • How to search and filter members in the admin
  • How to ban, silence, or space-ban a member
  • How to override a member's trust level manually
  • Which capability gates access to this screen

Go to Jetonomy → Users to access this screen.

Required Capability

The Users screen and its actions are gated by:

Action Required Capability
View the Users page jetonomy_moderate
Ban or unban a member jetonomy_moderate
Silence a member jetonomy_moderate
Change trust level jetonomy_manage_settings
Search users (AJAX picker in other admin screens) jetonomy_manage_spaces

WordPress Administrators and Editors have jetonomy_moderate automatically. See Settings → Permissions → WordPress Role Mapping for the full capability table.

Searching and Filtering

The toolbar at the top of the Users screen has two controls:

  1. Search box - searches by username or display name. Type at least two characters; results update on submit.
  2. Trust Level filter - dropdown to show only members at a specific trust level (0 = New through 5 = Elder). Select "All Trust Levels" to show everyone.

The paginator at the bottom shows total user count and lets you move between pages.

User Table Columns

Column Description
Username Avatar, login name, and row action links
Display Name Public display name
Trust Level Badge showing current level (0-5)
Reputation Total reputation score
Posts Post count
Replies Reply count
Joined Registration date (relative)
Last Seen Most recent login (relative)

Row Actions

Hover over a row to reveal the action links under the username.

View Profile

Opens the standard WordPress user edit screen for that member. This is the right place to change their email, reset their password, or modify their WordPress role.

Change Trust Level

Opens an inline dropdown directly in the table row. Choose any level from 0 (New) to 5 (Elder) and click Save. The change takes effect immediately and is logged in the activity log.

Trust levels 0 through 3 are normally earned automatically by the trust cron job. Use this override to:

  • Promote a long-standing member to Level 4 or 5 (manual tiers)
  • Demote a member who behaved poorly but has not yet triggered enough flags for automatic demotion
  • Grant Level 2 to a known member immediately after import

Ban

Opens the Ban User modal. Three options:

Type Effect
Global Ban Member cannot log in to Jetonomy at all
Silence Member can log in and read but cannot post, reply, or vote
Space Ban Member is banned from a single space (enter the space ID in the form)

Duration options: Permanent, 1 Day, 7 Days, 30 Days.

A Reason field is optional but recommended - the reason is stored in the wp_jt_restrictions table and visible if you query restrictions via WP-CLI or the REST API.

Silence (quick action)

The row also exposes a Silence shortcut link that applies a permanent global silence immediately without opening the modal. Use the Ban modal if you want a temporary silence or a reason.

Removing a Ban

Bans are not managed through the Users screen directly after they are created. To remove a ban:

WP-CLI:

wp --path="/path/to/wordpress" jetonomy user unban <user_id>

REST API (site admin):

DELETE /jetonomy/v1/users/<user_id>/restrictions/<restriction_id>

Temporary bans (1d, 7d, 30d) expire automatically - you do not need to remove them manually.

Trust Level Reference

Level Name How Earned
0 New Default on registration
1 Basic Auto - light participation
2 Member Auto - consistent participation
3 Regular Auto - high engagement
4 Leader Manual only (moderator or admin)
5 Elder Manual only (admin only)

Thresholds for levels 1-3 are configured on Settings → Permissions → Trust Level Thresholds.

What's Next?

Manage posts and replies from the content list view.

Admin Content →

The Posts and Replies admin screen lets you view, search, edit, and moderate every piece of content in your community without visiting the front end.

What You Will Learn

  • How to find and filter community posts and replies
  • How to inline-edit a post title or body
  • How to trash, approve, or mark content as spam
  • How bulk actions work

Go to Jetonomy → Posts and Replies to access this screen.

Required Capability

Viewing and moderating content requires jetonomy_moderate. WordPress Administrators and Editors receive this capability automatically.

The Toolbar

The filter bar at the top of the screen has four controls that can be combined freely:

Control What it filters
Space filter Show only posts from a specific space
Status filter All, Published, Pending, Spam, or Trash
Search box Searches post titles
Clear button Appears when any filter is active; resets all to defaults

The counter in the toolbar shows which range of results you are viewing and the total matching count.

Post Table Columns

Column Description
(checkbox) Select row for bulk actions
Title Post title, status badge, reply count link, row actions
Space The space the post belongs to
Author Display name of the author
Status Published, Pending, Spam, or Trash
Replies Reply count, linking to a filtered view of replies for that post
Views View count
Date Time elapsed since creation

Row Actions

Hover a row to reveal its action links.

Edit (inline)

Click Edit to expand an inline edit panel directly in the table row. You can change:

  • Post title (up to 255 characters)
  • Post body (full HTML content)

Click Save to apply the change. Click Cancel to collapse the panel without saving.

Trash

Moves the post to Trash status. Trashed posts are hidden from the front end but remain in the database. Use the Status filter to view trashed posts and restore them with the Restore action that appears in their row.

Spam

Marks the post as spam. Spam posts are hidden from the front end. The author's reputation is not automatically adjusted by this action.

View

Opens the post on the front end in a new tab. Only appears when the post has a valid space and slug.

Bulk Actions

To apply an action to multiple posts at once:

  1. Check the boxes next to the posts you want to affect. Check the header checkbox to select all visible rows.
  2. Choose Approve, Move to Trash, or Mark as Spam from the bulk action dropdown.
  3. Click Apply.

A confirmation prompt appears before the action runs. The spinner in the toolbar indicates the request is in flight.

Tip: If a bulk action partially fails (some rows succeed, some do not), the page will report the count that succeeded. Refresh and re-apply to the remaining rows.

Viewing Replies

Clicking the reply count number for any post opens a filtered view showing only the replies for that post. Replies use the same toolbar, status filter, and inline-edit actions as posts.

Status Definitions

Status Meaning
Published Visible to all users with read access
Pending Created by a member at trust level 0 and awaiting first-post review
Spam Flagged or manually marked as spam
Trash Soft-deleted; not visible on the front end

Note: Pending posts are held for review when the anti-spam settings require first-post moderation. Once you approve a member's first post, subsequent posts from that member publish immediately.

What's Next?

Organize your community's spaces into categories.

Admin Categories →

Categories let you group related spaces under a shared label so members can browse a subset of your community without seeing everything at once.

What You Will Learn

  • How to create, edit, and delete categories
  • How category visibility works
  • How to reorder categories with drag and drop
  • How icons and colors work

Go to Jetonomy → Categories to access this screen.

Required Capability

Creating, editing, and deleting categories requires jetonomy_manage_spaces. WordPress Administrators and Editors receive this capability automatically.

Page Layout

The Categories screen is split into two panels side by side:

  • Left - Add New Category form for creating a new category
  • Right - Categories table listing existing categories and their children

Creating a Category

Fill in the Add New Category form and click Add Category.

Field Required Notes
Name Yes Displayed in navigation and on the category page
Slug No Auto-generated from the name if left blank. Used in URLs.
Description No Optional text shown on the category listing page
Parent Category No Nest this category under an existing one. Two levels max.
Visibility No Controls who can see this category (see Visibility section below)
Icon No Choose from the Lucide icon picker
Color No Color swatch displayed next to the category name in navigation

Visibility Options

Option Who can see the category
Public All visitors (including logged-out) when guest access is on
Private Logged-in members only
Hidden Not shown in navigation or listings; direct URL still works

The category's visibility does not override the visibility of individual spaces within it. A public category can contain private spaces.

Editing a Category

Click Edit in the row actions under any category name. An Edit Category modal opens with the same fields as the creation form. Make your changes and click Update Category.

Deleting a Category

Click Delete in the row actions. A browser confirmation prompt appears before the delete runs.

Warning: Deleting a category does not delete the spaces inside it. Spaces that belonged to the deleted category lose their category assignment and move to "uncategorized." There is no undo - confirm you have reassigned or are okay losing the grouping before deleting.

Reordering Categories

Drag the handle icon at the far left of any row to reorder categories. The order is saved automatically when you drop the row. Child categories follow their parent when the parent moves.

Child Categories

Set Parent Category when creating or editing a category to nest it under an existing top-level category. The table renders children indented below their parent. Two nesting levels are supported.

Search

Use the search box in the table toolbar to filter categories by name. Clear the search to see all categories again. The search does not affect the Add New Category form.

Rows Per Page

The dropdown in the table toolbar controls how many categories appear per page (20, 50, or 100). The selection submits the form immediately.

What's Next?

Enable or disable Jetonomy Pro extensions if you have the Pro plugin active.

Extensions →

The Extensions screen is your control panel for turning individual Jetonomy Pro features on or off. It appears in the Jetonomy menu only when Jetonomy Pro is active.

What You Will Learn

  • Where the Extensions screen lives and who can access it
  • How to enable or disable an extension
  • What happens to data when you disable an extension

Go to Jetonomy → Extensions to access this screen. This menu item is added by Jetonomy Pro and is not visible on free-only installs.

Required Capability

Toggling extensions requires manage_options. Only WordPress Administrators can enable or disable extensions.

The Extension Grid

Extensions are displayed as cards in a grid. Each card shows:

  • Extension name and category tag
  • Version number
  • Short description of what the extension does
  • Toggle switch (on = enabled, off = disabled)

Active extensions have a highlighted card border. Inactive extensions appear muted.

Filtering by Category

Use the filter buttons above the grid to show only extensions in a specific category. Available categories include Communication, Engagement, Administration, Moderation, Gamification, Content, Integration, Branding, SEO, and AI.

Click All to return to the full list.

Enabling an Extension

Click the toggle switch on the card. The page reloads and the extension is now active. On first enable, the extension's activate() method runs - this creates any database tables the extension needs and schedules any cron hooks.

Disabling an Extension

Click the toggle switch on an active card. The page reloads and the extension is now off. The extension's deactivate() method runs - this removes scheduled cron hooks and deregisters rewrite rules.

Note: Disabling an extension does not delete its data. Database tables and stored options are preserved. Re-enabling the extension restores full functionality with all existing data intact.

Available Extensions

Extension Category What It Adds
Private Messaging Communication Direct and group message threads at /community/messages/
Analytics Administration Engagement graphs, top spaces, top contributors
Emoji Reactions Engagement Per-post and per-reply emoji reaction picker
Polls Engagement Inline polls inside posts
Custom Badges Gamification Award and display custom achievement badges
Custom Fields Content Add custom profile and post fields
Webhooks Integration Outgoing webhooks on community events
Advanced Moderation Moderation Keyword rules, regex patterns, auto-action on matched content
White Label Branding Replace Jetonomy branding with your own
Email Digest Communication Daily and weekly summary emails for members
Web Push Communication Browser push notifications
SEO Pro SEO Schema.org markup, sitemaps, canonical handling
Reply By Email Communication Members reply to threads by replying to notification emails
AI Integration AI Spam detection, post summarization, semantic search, content suggestions

For detailed setup instructions for each extension, see the Pro Features documentation.

What's Next?

For the full feature documentation for each Pro extension, see the Pro Features section.

Pro Features →

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/{id}/privileged-members Public List admins and moderators of a space

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
POST /posts/{id}/idea-status Space Moderator Set the roadmap status on an idea-type post (planned, in_progress, shipped, declined)
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
POST /replies/{id}/split Moderator / Admin Split this reply into a new standalone post

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
DELETE /posts/{id}/vote Logged in Remove vote from a post
POST /replies/{id}/vote Logged in Cast or toggle a reply vote
DELETE /replies/{id}/vote Logged in Remove vote from a reply

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
DELETE /notifications/{id} Logged in (own only) Delete a single notification
POST /notifications/bulk Logged in (own only) Bulk mark-as-read or delete a batch of notifications

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
POST /subscriptions Logged in Create a subscription to a space or post
DELETE /subscriptions/{id} Logged in Remove a subscription

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 (global ban, space ban, or silence)
DELETE /moderation/ban/{id} Moderator Remove a ban
GET /spaces/{id}/moderation/flags Space Admin List flags filed within a specific space
POST /spaces/{id}/moderation/flags/{flag_id}/resolve Space Admin Resolve a flag within a specific space
POST /spaces/{id}/moderation/{action}/{type}/{obj_id} Space Admin Moderate content in a specific space (action: approve, spam, or trash; type: post or reply)

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
PATCH /users/me Logged in (own account) Update 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
GET /users/suggest Public Typeahead - suggest users by name or login prefix

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

Bookmarks

Method Route Auth Description
GET /bookmarks Logged in List the current user's bookmarked posts
POST /bookmarks Logged in Toggle a bookmark on a post (adds if absent, removes if present)
DELETE /bookmarks/{post_id} Logged in Remove a specific bookmark by post ID

Media

Method Route Auth Description
POST /media Logged in (jetonomy_upload_media) Upload an image, video, or file attachment

oEmbed

Method Route Auth Description
GET /oembed Public oEmbed endpoint for forum posts - returns embed metadata for the given post URL

Authentication and Registration

These endpoints are unauthenticated and rate-limited. They are used by the headless frontend or when the native WordPress login form is not in use.

Method Route Auth Description
POST /auth/login Public (rate limited) Log in and receive an authentication cookie
POST /auth/register Public (rate limited) Register a new user account
POST /auth/lost-password Public (rate limited) Request a password reset email
GET /auth/verify-email Public Verify an email confirmation token
POST /auth/resend-verification Public (rate limited) Resend the email verification message

Admin

These endpoints require the manage_options capability (administrators only).

Method Route Auth Description
POST /admin/recount Admin (manage_options) Rebuild all denormalized counters (reply counts, vote scores, post counts)
POST /admin/users/trust-level Admin (manage_options) Manually set a user's trust level

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();

Polls (polls extension)

Method Route Auth Description
GET /posts/{post_id}/poll Public Get a post's poll and current results
POST /posts/{post_id}/poll Logged in (can post) Create a poll on a post
POST /polls/{id}/vote Logged in Cast a vote on a poll option
DELETE /polls/{id}/vote Logged in Retract a vote
PATCH /polls/{id} Author / Moderator Update or close a poll

Reactions (reactions extension)

Method Route Auth Description
GET /posts/{id}/reactions Public List reactions on a post
POST /posts/{id}/reactions Logged in Toggle an emoji reaction on a post
GET /replies/{id}/reactions Public List reactions on a reply
POST /replies/{id}/reactions Logged in Toggle an emoji reaction on a reply

Custom Badges (custom-badges extension)

Method Route Auth Description
GET /badges Logged in List all defined badges
POST /badges Admin (manage_options) Create a new badge definition
PATCH /badges/{id} Admin (manage_options) Update a badge definition
DELETE /badges/{id} Admin (manage_options) Delete a badge definition
POST /users/{user_id}/badges Admin (manage_options) Manually award a badge to a user

Custom Fields (custom-fields extension)

Method Route Auth Description
GET /custom-fields Logged in List all custom field definitions
POST /custom-fields Admin (manage_options) Create a custom field definition
PATCH /custom-fields/{id} Admin (manage_options) Update a custom field definition
DELETE /custom-fields/{id} Admin (manage_options) Delete a custom field definition
POST /custom-field-values Logged in Set a custom field value on a post, reply, or user profile

Email Digest (email-digest extension)

Method Route Auth Description
GET /users/me/digest-preferences Logged in Get the current user's digest frequency and topic preferences
PATCH /users/me/digest-preferences Logged in Update digest frequency and topics
POST /admin/digest/test jetonomy_manage_settings Send a test digest email
GET /admin/digest/stats jetonomy_manage_settings Digest delivery statistics

Webhooks (webhooks extension)

Method Route Auth Description
GET /webhooks Admin (manage_options) List all registered webhook endpoints
POST /webhooks Admin (manage_options) Register a new webhook endpoint
DELETE /webhooks/{id} Admin (manage_options) Delete a webhook endpoint

White Label (white-label extension)

Method Route Auth Description
GET /settings/white-label Admin (manage_options) Get current white-label settings (logo, colors, footer text)
PATCH /settings/white-label Admin (manage_options) Save white-label settings

AI (ai extension)

All AI endpoints require the manage_options capability.

Method Route Auth Description
GET /ai/providers Admin List configured AI providers and their status
POST /ai/suggestions Admin Generate AI-powered content suggestions
POST /ai/spam-detection Admin Run AI spam detection on a piece of content
GET /ai/usage Admin Get per-request AI usage statistics
GET /ai/usage/summary Admin Get monthly usage summary grouped by provider

Web Push (web-push extension)

Method Route Auth Description
POST /push/subscribe Logged in Register a browser push subscription
DELETE /push/subscribe Logged in Remove a push subscription
GET /push/vapid-key Logged in Get the public VAPID key needed to subscribe
GET /push/service-worker.js Public Serves the push service-worker script

SEO Pro (seo-pro extension)

Method Route Auth Description
GET /spaces/{id}/seo Space Admin Get SEO metadata for a space (title, description, OG image)
POST /spaces/{id}/seo Space Admin Save SEO metadata for a space

Reply by Email (reply-by-email extension)

Method Route Auth Description
POST /reply-by-email/process Shared secret (webhook) Inbound webhook endpoint for processing email replies - authenticated via a shared secret header, not a user session

Advanced Moderation (advanced-moderation extension)

All advanced moderation endpoints require the manage_options capability.

Method Route Auth Description
GET /moderation/rules Admin List all auto-moderation rules
POST /moderation/rules Admin Create a new auto-moderation rule
PATCH /moderation/rules/{id} Admin Update an auto-moderation rule
DELETE /moderation/rules/{id} Admin Delete an auto-moderation rule
GET /moderation/rules/{id}/stats Admin Get trigger statistics for a specific rule

Analytics - additional endpoint (analytics extension)

Method Route Auth Description
GET /analytics/diff-report Admin (manage_options) Per-metric drift report comparing the direct-query path against the event-driven aggregate path; returns from_query, from_events, drift_pct, and within_tolerance for each metric

Community Announcements (site-announcements extension)

Namespace: these routes live under jetonomy-pro/v1, not jetonomy/v1.

All routes require the manage_options or jetonomy_manage_spaces capability (administrators by default).

Method Route Description
GET /site-announcements List the currently pinned (announced) posts
POST /site-announcements/{id} Pin a post site-wide (capped at 5; returns 400 past the cap)
DELETE /site-announcements/{id} Remove a post from the announcements
// Pin post 395 to the whole community
await fetch( '/wp-json/jetonomy-pro/v1/site-announcements/395', {
    method:  'POST',
    headers: { 'X-WP-Nonce': nonce },
} );

This is distinct from the free space-level pin (POST /posts/{id}/pin), which only stickies a topic within its own space.


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 58 hooks in the free plugin and 9 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 via REST.

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 );

For a hook that fires on every insert path (REST, admin, CLI, abilities, import) see jetonomy_post_created below.


jetonomy_post_created

Fires from the Post model right after a row is inserted, so it covers every insert path - REST, admin AJAX, WP-CLI, Abilities, imports. Use this when you want to score the creation event itself regardless of how it was triggered.

Fires for every status. Listeners that only care about published posts should inspect $context['status'].

Parameters

Parameter Type Description
$post_id int Inserted post ID
$space_id int Parent space ID (0 if unset)
$user_id int Author user ID (0 if unset)
$context array The inserted column data (status, post_type, idea_status, slug, etc.)

Source: includes/models/class-post.php

// WB Gamification example: award points the moment a post lands, not when it gets upvoted.
add_action( 'jetonomy_post_created', function( int $post_id, int $space_id, int $user_id, array $context ) {
    if ( $user_id <= 0 || 'publish' !== ( $context['status'] ?? 'publish' ) ) {
        return;
    }
    wb_gam_award_points( $user_id, 'forum_post_created', [
        'post_id'  => $post_id,
        'space_id' => $space_id,
    ] );
}, 10, 4 );

jetonomy_after_create_reply

Fires immediately after a new reply is saved successfully via REST. 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 );

For a hook that fires on every insert path see jetonomy_reply_created below.


jetonomy_reply_created

Mirrors jetonomy_post_created for the reply path. Fires from the Reply model so every insert path (REST, admin AJAX, CLI, Abilities, imports) is covered.

Parameters

Parameter Type Description
$reply_id int Inserted reply ID
$post_id int Parent post ID (0 if unset)
$user_id int Author user ID (0 if unset)
$context array The inserted column data (status, parent_id, content, etc.)

Source: includes/models/class-reply.php

add_action( 'jetonomy_reply_created', function( int $reply_id, int $post_id, int $user_id, array $context ) {
    if ( $user_id <= 0 || 'publish' !== ( $context['status'] ?? 'publish' ) ) {
        return;
    }
    wb_gam_award_points( $user_id, 'forum_reply_created', [
        'reply_id' => $reply_id,
        'post_id'  => $post_id,
    ] );
}, 10, 4 );

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 via REST. Use this for receiver-side analytics; for voter-side gamification see jetonomy_vote_cast / jetonomy_vote_retracted below.

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 );

jetonomy_vote_cast

Fires when a voter casts a new vote (or flips an existing one). Used by gamification to reward the voter directly. Reputation handles the receiver; this hook is the missing voter-side signal.

When a voter flips from upvote to downvote (or vice versa) a jetonomy_vote_retracted fires for the old value, then jetonomy_vote_cast fires for the new value.

Parameters

Parameter Type Description
$vote_type int Raw vote value the voter chose (1 for upvote, -1 for downvote)
$object_type string 'post' or 'reply'
$object_id int Target object ID
$voter_id int Voting user ID

Source: includes/models/class-vote.php

// "Voted 10x this week" challenge.
add_action( 'jetonomy_vote_cast', function( int $vote_type, string $object_type, int $object_id, int $voter_id ) {
    wb_gam_award_points( $voter_id, 'forum_vote_cast', [
        'vote_type'   => $vote_type,
        'object_type' => $object_type,
        'object_id'   => $object_id,
    ] );
}, 10, 4 );

jetonomy_vote_retracted

Fires when a voter retracts an existing vote (clicks the same arrow again or flips to the opposite arrow).

Parameters

Parameter Type Description
$vote_type int Raw value of the retracted vote (1 or -1)
$object_type string 'post' or 'reply'
$object_id int Target object ID
$voter_id int Voting user ID

Source: includes/models/class-vote.php

add_action( 'jetonomy_vote_retracted', function( int $vote_type, string $object_type, int $object_id, int $voter_id ) {
    wb_gam_revoke_points( $voter_id, 'forum_vote_cast', [
        'object_type' => $object_type,
        'object_id'   => $object_id,
    ] );
}, 10, 4 );

Ideas / Roadmap

jetonomy_idea_status_changed

Fires after an idea's roadmap status changes (e.g. plannedin_progressshipped). Use this to reward the post author on every transition, not just planned.

Parameters

Parameter Type Description
$post_id int Post ID
$new_status string New idea_status value
$old_status string Previous value (empty if unset)
$actor_id int Moderator who changed it
$author_id int Original post author (0 if unset) - added so listeners can reward the author without a second lookup

Source: includes/api/class-posts-controller.php

add_action( 'jetonomy_idea_status_changed', function( int $post_id, string $new_status, string $old_status, int $actor_id, int $author_id ) {
    if ( $author_id <= 0 || $author_id === $actor_id ) {
        return; // Author can't farm their own status changes.
    }
    if ( 'shipped' === $new_status ) {
        wb_gam_award_points( $author_id, 'idea_shipped', [ 'post_id' => $post_id ] );
    }
}, 10, 5 );

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_pre_change

Filter (not action). Lets you intercept an automatic trust-level promotion before it is written - to veto promotions for sandboxed users, fast-track a cohort during onboarding, or apply per-tenant ladder rules.

Only fires on automatic promotion paths: the daily cron evaluator and wp jetonomy trust-evaluate. Manual admin/CLI overrides bypass this filter on purpose so admins can always force-set a level.

Returning the user's current level short-circuits the write (no DB update, no jetonomy_trust_level_changed action).

Parameters

Parameter Type Description
$new_level int Level the evaluator chose (the proposed level)
$user_id int Target user ID
$stats array Stats fed to the evaluator: post_count, days_active, reputation, replies_received

Returns: int - the level to actually write.

Source: includes/class-cron.php, includes/class-cli.php

// WB Gamification example: veto auto-promotion for users in the sandbox.
add_filter( 'jetonomy_trust_level_pre_change', function( int $new_level, int $user_id, array $stats ): int {
    if ( get_user_meta( $user_id, 'wb_gam_sandboxed', true ) ) {
        $current = (int) \Jetonomy\Models\UserProfile::find( $user_id )->trust_level;
        return $current; // short-circuit the write.
    }
    return $new_level;
}, 10, 3 );

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
$space_id int ID of the space joined
$user_id int WP user ID of the new member
$role string The member's role in the space (e.g. member)

Source: includes/models/class-space-member.php

// Note the argument order: $space_id comes first, then $user_id, then $role.
add_action( 'jetonomy_user_joined_space', function( int $space_id, int $user_id, string $role ) {
    // Auto-subscribe the user to a MailChimp list tied to this space.
    my_mailchimp_subscribe( $user_id, "space_{$space_id}" );
}, 10, 3 );

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

jetonomy_max_space_pins (filter)

Filters the maximum number of topics that can be pinned (made sticky) in a single space. Returning 0 (or a negative value) disables the cap entirely. Default is 3.

Parameters

Parameter Type Description
$max int Default maximum (3)
$space_id int The space the pin is being attempted in
// Allow up to 5 pinned topics in every space.
add_filter( 'jetonomy_max_space_pins', fn() => 5 );

// Different caps per space.
add_filter( 'jetonomy_max_space_pins', function ( $max, $space_id ) {
	return 10 === (int) $space_id ? 8 : $max;
}, 10, 2 );

This only governs the space-level pin (is_sticky). Community announcements ("Pin to community", Pro) have their own separate cap.


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_post_card_after_badges

Fires right after the built-in status badges (sticky/Pinned, private) so add-ons can append their own markers - for example the Pro "Announcement" badge for community-pinned posts. As of 1.4.4 this fires in both the listing card and the single-post header, so a badge added here appears consistently on both surfaces.

Parameters

Parameter Type Description
$post object The post row being rendered
$space object|null The post's space, if loaded

Source: templates/partials/post-card.php, 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


Registration & Boot Hooks

jetonomy_admin_license_tab_content

Fires inside the Settings page License tab placeholder. Use this to render a license activation form or status block.

Parameters

None.

Source: includes/admin/views/settings.php


User Lifecycle Hooks

jetonomy_user_registered

Fires after a new user account is created via the REST registration endpoint.

Parameters

Parameter Type Description
$user_id int WP user ID of the newly registered user

Source: includes/api/class-auth-controller.php


jetonomy_user_pending_verification

Fires after a new user is created but email verification is required and the account is in a pending state.

Parameters

Parameter Type Description
$user_id int WP user ID of the pending user

Source: includes/api/class-auth-controller.php


jetonomy_email_verified

Fires after a user successfully verifies their email address via the verification token link.

Parameters

Parameter Type Description
$user_id int WP user ID of the verified user

Source: includes/api/class-auth-controller.php


jetonomy_verification_reminder_sent

Fires after the cron-driven single-shot verification reminder email is dispatched to an unverified user.

Parameters

Parameter Type Description
$user_id int WP user ID of the nudged user
$user WP_User WP_User object for the nudged user

Source: includes/notifications/class-verification-reminder.php


Content Lifecycle Hooks

jetonomy_before_create_post

Filter (not action). Runs immediately before a new post row is inserted. Modify the data array to change what gets written, or use it to pre-process content.

Parameters

Parameter Type Description
$data array Column data about to be inserted (keys: title, content, status, space_id, author_id, post_type, etc.)
$author_id int Author user ID (convenience copy from $data['author_id'])
$space_id int Target space ID (convenience copy from $data['space_id'])

Return: array Modified data array.

Source: includes/models/class-post.php


jetonomy_before_create_reply

Filter (not action). Runs immediately before a new reply row is inserted.

Parameters

Parameter Type Description
$data array Column data about to be inserted (keys: content, status, post_id, author_id, parent_id, etc.)
$author_id int Author user ID (convenience copy from $data['author_id'])
$post_id int Parent post ID (convenience copy from $data['post_id'])

Return: array Modified data array.

Source: includes/models/class-reply.php


jetonomy_before_delete_post

Filter (not action). Fires before a post is permanently deleted. Return false to abort the delete.

Parameters

Parameter Type Description
$proceed bool true to allow deletion; return false to cancel
$post_id int ID of the post about to be deleted

Return: bool

Source: includes/models/class-post.php


jetonomy_before_delete_reply

Filter (not action). Fires before a reply is permanently deleted. Return false to abort.

Parameters

Parameter Type Description
$proceed bool true to allow deletion
$reply_id int ID of the reply about to be deleted

Return: bool

Source: includes/models/class-reply.php


jetonomy_reply_unaccepted

Fires after the accepted-answer mark is removed from a reply.

Parameters

Parameter Type Description
$reply_id int ID of the reply that was un-accepted
$post_id int ID of the parent post

Source: includes/api/class-replies-controller.php


jetonomy_scheduled_post_published

Fires after a scheduled post is automatically published by the cron handler when its published_at datetime passes.

Parameters

Parameter Type Description
$post_id int ID of the published post
$space_id int ID of the space the post belongs to

Source: includes/models/class-post.php


Moderation & Flagging Hooks

jetonomy_flag_created

Fires after a new flag is saved against a post or reply.

Parameters

Parameter Type Description
$flag_id int ID of the created flag record
$object_type string 'post' or 'reply'

Source: includes/api/class-moderation-controller.php


jetonomy_flag_resolved

Fires after a flag is resolved (approved, dismissed, or actioned). For a version that includes the full flag object see jetonomy_after_resolve_flag below.

Parameters

Parameter Type Description
$flag_id int ID of the resolved flag
$status string Resolution status (e.g. 'approved', 'dismissed')
$user_id int ID of the moderator who resolved it

Source: includes/moderation/class-moderation-service.php


jetonomy_after_resolve_flag

Fires after a flag is resolved and provides the full flag object plus context. Counterpart to jetonomy_flag_resolved - use this when you need to read the flag's fields without a second lookup.

Parameters

Parameter Type Description
$flag object The full flag row object (post-resolution state)
$context array Associative array with keys status (string) and user_id (int)

Source: includes/moderation/class-moderation-service.php


Space Membership Hooks

jetonomy_before_join_space

Filter (not action). Fires before a user is added to a space. Return false to block the join.

Parameters

Parameter Type Description
$proceed bool true to allow the join
$user_id int User attempting to join
$space_id int Target space ID
$role string Role being assigned (e.g. 'member', 'admin')

Return: bool

Source: includes/models/class-space-member.php


jetonomy_user_left_space

Fires after a user is removed from a space.

Parameters

Parameter Type Description
$space_id int ID of the space the user left
$user_id int WP user ID of the departing member

Source: includes/models/class-space-member.php


jetonomy_space_member_joined

Alias of jetonomy_user_joined_space without the role argument, matching the Pro webhooks listener contract. Fires at the same time as jetonomy_user_joined_space.

Parameters

Parameter Type Description
$space_id int ID of the space
$user_id int WP user ID of the new member

Source: includes/models/class-space-member.php


jetonomy_space_member_left

Alias of jetonomy_user_left_space, matching the Pro webhooks listener contract.

Parameters

Parameter Type Description
$space_id int ID of the space
$user_id int WP user ID of the departing member

Source: includes/models/class-space-member.php


jetonomy_join_request_created

Fires after a join request is submitted for a private space.

Parameters

Parameter Type Description
$space_id int ID of the space being requested
$user_id int ID of the requesting user
$message string Optional message submitted with the request (empty string if none)

Source: includes/api/class-spaces-controller.php


Voting Hooks

jetonomy_before_vote

Filter (not action). Fires before a vote is recorded. Return false to block the vote entirely.

Parameters

Parameter Type Description
$proceed bool true to allow the vote
$user_id int Voting user ID
$object_type string 'post' or 'reply'
$object_id int Target object ID
$value int Vote value: 1 for upvote, -1 for downvote

Return: bool

Source: includes/models/class-vote.php


Trust & Reputation Hooks

jetonomy_reputation_points_for

Filter (not action). Filters the point value for a specific reputation action before it is applied. Allows per-site tuning of the reputation ladder without editing plugin settings.

Parameters

Parameter Type Description
$points int Default point value for the action
$action string Action slug (e.g. 'post_upvoted', 'reply_accepted', 'flag_validated')

Return: int

Source: includes/trust/class-reputation.php

add_filter( 'jetonomy_reputation_points_for', function( int $points, string $action ): int {
    // Double the reward for accepted answers on this site.
    if ( 'reply_accepted' === $action ) {
        return $points * 2;
    }
    return $points;
}, 10, 2 );

Email & Notification Filter Hooks

jetonomy_email_subject

Filter (not action). Filters the email subject line before the notification is sent.

Parameters

Parameter Type Description
$subject string Rendered subject string
$type string Notification type slug (e.g. 'reply_to_post', 'mention', 'accepted_answer')
$user WP_User Notification recipient

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_email_body

Filter (not action). Filters the plain-text email body before the notification is sent.

Parameters

Parameter Type Description
$body string Plain-text body string
$type string Notification type slug
$user WP_User Notification recipient

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_email_html

Filter (not action). Filters the fully rendered HTML of the notification email just before it is passed to wp_mail().

Parameters

Parameter Type Description
$html string Full rendered HTML string
$type string Notification type slug
$user WP_User Notification recipient

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_email_headers

Filter (not action). Filters the headers array passed to wp_mail() for all notification emails. Note: for adding a Reply-To header for reply-by-email support, use jetonomy_notification_email_headers instead (also available).

Parameters

Parameter Type Description
$headers array Array of mail header strings
$type string Notification type slug
$user WP_User Notification recipient

Return: array

Source: includes/notifications/class-notifier.php


jetonomy_email_logo_url

Filter (not action). Filters the logo URL rendered inside notification emails. Return an empty string to suppress the logo.

Parameters

Parameter Type Description
$url string Logo URL (empty string if none configured)
$type string Notification type slug providing context for per-type overrides

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_email_accent_color

Filter (not action). Filters the hex accent color used in notification email templates for buttons and highlights.

Parameters

Parameter Type Description
$color string Hex color string (e.g. '#3B82F6')
$type string Notification type slug

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_email_template_context

Filter (not action). Filters the full template context array before it is passed to the email template renderer. Use this to inject extra variables into any notification email template.

Parameters

Parameter Type Description
$ctx array Template variables array (keys vary by notification type)
$type string Notification type slug
$user WP_User Notification recipient

Return: array

Source: includes/notifications/class-notifier.php


jetonomy_email_template_path

Filter (not action). Filters the resolved filesystem path to the email template file before it is loaded.

Parameters

Parameter Type Description
$path string Absolute path to the email template .php file
$type string Notification type slug

Return: string

Source: includes/notifications/class-notifier.php


jetonomy_disposable_email_domains

Filter (not action). Filters the list of blocked disposable email domains used during registration validation.

Parameters

Parameter Type Description
$domains array Array of blocked domain strings (e.g. ['mailinator.com', 'guerrillamail.com'])

Return: array

Source: includes/api/class-auth-controller.php


REST Response Filter Hooks

jetonomy_rest_prepare_post

Filter (not action). Filters the prepared post data array before it is returned in any REST response. Use this for fine-grained control over individual fields. For appending extension-level payload use jetonomy_post_response instead.

Parameters

Parameter Type Description
$data array Prepared REST response data for the post
$post object Raw post row object
$request WP_REST_Request|null The current request, or null in non-request contexts

Return: array

Source: includes/api/class-posts-controller.php


jetonomy_rest_prepare_reply

Filter (not action). Filters the prepared reply data array before it is returned in any REST response.

Parameters

Parameter Type Description
$data array Prepared REST response data for the reply
$reply object Raw reply row object
$request WP_REST_Request|null The current request, or null in non-request contexts

Return: array

Source: includes/api/class-replies-controller.php


jetonomy_rest_prepare_space

Filter (not action). Filters the prepared space data array before it is returned in any REST response.

Parameters

Parameter Type Description
$data array Prepared REST response data for the space
$space object Raw space row object
$request WP_REST_Request|null The current request, or null

Return: array

Source: includes/api/class-spaces-controller.php


jetonomy_rest_prepare_user

Filter (not action). Filters the prepared user data array before it is returned in any REST response.

Parameters

Parameter Type Description
$data array Prepared REST response data for the user
$wp_user WP_User WP user object
$request WP_REST_Request|null The current request, or null

Return: array

Source: includes/api/class-users-controller.php


jetonomy_rest_prepare_notification

Filter (not action). Filters the prepared notification data array before it is returned in any REST response.

Parameters

Parameter Type Description
$data array Prepared REST response data for the notification
$notification object Raw notification row object
$request WP_REST_Request|null The current request, or null

Return: array

Source: includes/api/class-notifications-controller.php


jetonomy_post_response

Filter (not action). Alias filter matching the Pro custom-fields listener contract. Lets extensions append per-post payload (custom field values, reactions, polls) to the REST response. The $context array carries object_type and object_id so generic extension handlers can route on type.

Parameters

Parameter Type Description
$data array Prepared REST response data
$context array Associative array with keys object_type ('post') and object_id (int)

Return: array

Source: includes/api/class-posts-controller.php


jetonomy_profile_response

Filter (not action). Lets extensions append data to user profile REST responses (custom fields, badges).

Parameters

Parameter Type Description
$data array Prepared profile response data
$context array Associative array with keys object_type ('user') and object_id (int)

Return: array

Source: includes/api/class-users-controller.php


jetonomy_oembed_response

Filter (not action). Filters the oEmbed response data for a forum post before it is returned.

Parameters

Parameter Type Description
$data array oEmbed response fields (title, url, thumbnail_url, etc.)
$post object The post row object
$space object The parent space row object

Return: array

Source: includes/api/class-oembed-controller.php


Link Preview Filter Hooks

jetonomy_link_preview_data

Filter (not action). Filters the extracted link preview data (OG metadata) for a URL before it is cached and returned.

Parameters

Parameter Type Description
$out array Extracted metadata: title, description, image, url, site_name
$url string The URL being previewed

Return: array

Source: includes/services/links/class-preview-service.php


jetonomy_link_preview_cache_ttl

Filter (not action). Filters the cache TTL (in seconds) for link preview results.

Parameters

Parameter Type Description
$ttl int Cache duration in seconds (default: 3600)
$url string The URL being previewed

Return: int

Source: includes/services/links/class-preview-service.php


jetonomy_link_preview_providers

Filter (not action). Filters the list of registered link preview provider handlers.

Parameters

Parameter Type Description
$providers array Ordered list of provider objects/callables

Return: array

Source: includes/services/links/class-preview-service.php


jetonomy_link_preview_user_agent

Filter (not action). Filters the User-Agent header used when fetching remote URLs for link preview extraction.

Parameters

Parameter Type Description
$ua string User-Agent string
$url string The URL about to be fetched

Return: string

Source: includes/services/links/class-html-fetcher.php


Template Filter Hooks

jetonomy_show_sidebar

Filter (not action). Return false to hide the sidebar entirely on the current page.

Parameters

Parameter Type Description
$show bool true by default

Return: bool

Source: templates/partials/sidebar.php


jetonomy_sidebar_auth_card

Filter (not action). Filters whether the default anonymous-user auth card is rendered in the sidebar. Return false (or an HTML string to replace it) to suppress or override it. Jetonomy Pro's White Label extension uses this to inject a custom card via jetonomy_sidebar_before.

Parameters

Parameter Type Description
$show bool true to render the default auth card

Return: bool

Source: includes/class-blocks.php


jetonomy_sidebar_about_after_meta

Fires inside the sidebar "About this space" card, after the meta row (member count, post count). Only fires on space-scoped pages where a space object is available.

Parameters

Parameter Type Description
$space object Current space row object

Source: templates/partials/sidebar.php


jetonomy_header_logo

Filter (not action). Filters the URL of the logo shown in the community header bar. Return an empty string to hide the logo.

Parameters

Parameter Type Description
$url string Default logo URL (empty string if none set)

Return: string

Source: includes/functions.php


jetonomy_footer_text

Filter (not action). Filters the text shown in the community footer. Return an empty string to suppress the footer text.

Parameters

Parameter Type Description
$text string Default footer text

Return: string

Source: includes/functions.php


jetonomy_new_post_submit_action

Filter (not action). Filters the Interactivity API store action name wired to the new-post form's submit event. Jetonomy Pro overrides this to 'actions.submitNewPostWithPoll' when the polls extension is active.

Parameters

Parameter Type Description
$action string Default action name: 'actions.submitNewPost'

Return: string

Source: templates/views/new-post.php


jetonomy_search_filters

Fires inside the search results page, inside the filter panel, after the built-in filters. Use this to render additional search filter controls.

Parameters

Parameter Type Description
$q string Current search query string
$filter string Active content-type filter ('all', 'posts', 'replies', 'spaces', 'users')
$context array Other active filter values: date_from, date_to, author_id, tag_slug, sort

Source: templates/views/search.php


Permissions Filter Hooks

jetonomy_space_role_permissions

Filter (not action). Filters the permissions array for a given space role. Use this to grant or revoke individual permissions for custom role configurations.

Parameters

Parameter Type Description
$permissions array Array of allowed action strings for the role (e.g. ['create_posts', 'vote', 'flag'])
$role string Role name: 'member', 'moderator', or 'admin'
$space_id int ID of the space the check applies to

Return: array

Source: includes/permissions/class-permission-engine.php


jetonomy_use_frontend_space_edit

Filter (not action). Filters whether the space settings form should be served via the frontend (in-page) UI rather than redirecting to the admin panel. Return false to force the admin redirect.

Parameters

Parameter Type Description
$use_frontend bool true by default (uses frontend form)
$space object Space row object

Return: bool

Source: includes/functions.php


Theme Integration Filter Hooks

jetonomy_theme_light_tokens

Filter (not action). Filters the CSS custom property token map written for light mode. Return a modified array to override or add tokens injected into the page <head>.

Parameters

Parameter Type Description
$tokens array Associative array of ['--token-name' => 'value'] pairs

Return: array

Source: includes/integrations/class-theme-integration.php


jetonomy_theme_dark_tokens

Filter (not action). Filters the CSS custom property token map written for dark mode.

Parameters

Parameter Type Description
$tokens array Associative array of ['--token-name' => 'value'] pairs

Return: array

Source: includes/integrations/class-theme-integration.php


Admin Filter Hooks

jetonomy_admin_footer_text

Filter (not action). Filters the footer text shown at the bottom of all Jetonomy admin pages (replaces the default WordPress "Thank you" text).

Parameters

Parameter Type Description
$text string Default footer text string

Return: string

Source: includes/admin/class-admin.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_dm_received action Fires once per recipient when a DM is delivered. Counterpart to jetonomy_pro_message_sent - lets you build "received first DM" or "active inbox" rules. Skipped for system messages. Params: $message_id (int), $conversation_id (int), $sender_id (int), $recipient_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)

jetonomy_pro_conversation_created

Fires after a new private conversation is created (both direct and group).

Parameters

Parameter Type Description
$conversation_id int ID of the newly created conversation
$user_id int ID of the user who created the conversation
$participants array Array of all participant user IDs (includes creator)

Source: jetonomy-pro/includes/extensions/private-messaging/class-extension.php


jetonomy_pro_message_notified

Fires after in-app and/or email notifications are dispatched to conversation participants for a new message. The $preview string is the truncated message preview sent in the notification.

Parameters

Parameter Type Description
$conversation_id int ID of the conversation
$sender_id int WP user ID of the message sender
$preview string Truncated plain-text preview of the message

Source: jetonomy-pro/includes/extensions/private-messaging/class-extension.php


jetonomy_pro_poll_created

Fires after a poll is created and attached to a post.

Parameters

Parameter Type Description
$poll_id int ID of the newly created poll
$post_id int ID of the post the poll is attached to
$user_id int ID of the user who created the poll

Source: jetonomy-pro/includes/extensions/polls/class-extension.php


jetonomy_pro_poll_voted

Fires after a user casts a vote on a poll.

Parameters

Parameter Type Description
$poll_id int ID of the poll
$user_id int ID of the voting user
$option_ids array Array of option IDs the user voted for (multi-select polls may have more than one)

Source: jetonomy-pro/includes/extensions/polls/class-extension.php


jetonomy_pro_poll_unvoted

Fires after a user removes their vote from a poll.

Parameters

Parameter Type Description
$poll_id int ID of the poll
$user_id int ID of the user who removed their vote

Source: jetonomy-pro/includes/extensions/polls/class-extension.php


jetonomy_pro_badge_earned

Fires after a badge is awarded to a user (both manual awards via the REST API and auto-awards from the daily cron evaluator).

Parameters

Parameter Type Description
$user_id int ID of the user who earned the badge
$badge_id int ID of the badge definition
$badge object Full badge row object (name, description, icon, criteria, etc.)

Source: jetonomy-pro/includes/extensions/custom-badges/class-extension.php


jetonomy_pro_field_created

Fires after a custom field definition is created.

Parameters

Parameter Type Description
$field_id int ID of the newly created custom field
$context array Creation context, including object_type ('post', 'user', or 'space') and the full field data

Source: jetonomy-pro/includes/extensions/custom-fields/class-extension.php


jetonomy_pro_reaction_toggled

Fires after an emoji reaction is added or removed. The $action parameter tells you which direction the toggle went.

Parameters

Parameter Type Description
$object_type string 'post' or 'reply'
$object_id int ID of the object that was reacted to
$emoji string The emoji slug or character (e.g. 'thumbs_up', '+1')
$user_id int ID of the reacting user
$action string 'added' or 'removed'

Source: jetonomy-pro/includes/extensions/reactions/class-extension.php


jetonomy_pro_ai_all_providers_failed

Fires when every configured AI provider has been tried and all returned an error or exception. Use this to log failures or alert an ops channel.

Parameters

Parameter Type Description
$feature string The feature that requested AI ('summary', 'spam', 'moderation', 'suggestion')
$exception \Throwable The last exception thrown

Source: jetonomy-pro/includes/extensions/ai/ (class-summarizer.php, class-spam-detector.php, class-moderator.php, class-suggester.php)


jetonomy_create_reply_from_email

Fired by the Pro Reply-by-Email extension to request that the free plugin create a reply from an inbound email. The free plugin listens to this action and routes it through the standard reply creation path so all free-side hooks (moderation, notifications, reputation) still fire.

Parameters

Parameter Type Description
$post_id int ID of the post to reply to
$user_id int ID of the user whose email was matched
$content string Cleaned plain-text or HTML content extracted from the email
$source string Source label; always 'reply_by_email' for replies created via this hook

Source: jetonomy-pro/includes/extensions/reply-by-email/class-extension.php


jetonomy_pro_reaction_icon_renderer

Filter (not action). Filters the renderer strategy used for a reaction icon. The default renderer is 'emoji', which uses the native platform emoji. Override with a custom renderer slug to swap in SVG icons or image sprites.

Parameters

Parameter Type Description
$renderer string Current renderer slug ('emoji' by default)
$slug string The reaction slug being rendered (e.g. '+1', 'heart')
$size int Requested icon size in pixels

Return: string Renderer slug to use.

Source: jetonomy-pro/includes/extensions/reactions/class-extension.php


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?

Jetonomy ships a full WP-CLI surface covering every core domain of the plugin: 14 command roots in the free plugin and 15 command roots in Jetonomy Pro, totalling 75+ subcommands across both plugins.

All free commands live under wp jetonomy <subject> <subcommand>. All Pro commands live under wp jetonomy-pro <subject> <subcommand> (note the separate root - Pro commands require both plugins active).

On this local dev install you must prefix every wp call with --path:

wp --path="/path/to/wp" jetonomy space list

Customers running from their WordPress root directory can omit --path.


Free Commands

category

Manage top-level categories that group spaces.

Subcommand Description
create Create a new category
list List all categories
update <id> Update category fields
delete <id> Delete a category

Flags - create: --name=<name> --slug=<slug> [--description=<text>] [--parent=<id>] [--format=<format>]

Flags - update: [--name=<name>] [--slug=<slug>] [--description=<text>] [--parent=<id>] [--sort=<order>] [--format=<format>]

wp jetonomy category create --name="General" --slug=general
wp jetonomy category create --name="Support" --slug=support --parent=1
wp jetonomy category update 3 --name="Renamed" --sort=10
wp jetonomy category list --format=json

space

Create and manage spaces (forums, Q&A boards, ideas boards, feeds).

Subcommand Description
create Create a new space
list List spaces
update <id> Update space settings
delete <id> Delete a space
add-member Add a member to a space
remove-member Remove a member from a space

Flags - create: --title=<title> --slug=<slug> --category=<id> [--description=<text>] [--type=<type>] [--visibility=<vis>] [--join-policy=<policy>] [--format=<format>]

Types: forum (default), qa, ideas, feed. Visibility: public, private, hidden. Join policy: open, approval, invite.

Flags - update: All optional - [--title=<title>] [--description=<text>] [--type=<type>] [--visibility=<vis>] [--join-policy=<policy>] [--status=<status>] [--format=<format>]

wp jetonomy space create --title="General" --slug=general --category=1
wp jetonomy space create --title="Q&A" --slug=qa --category=1 --type=qa --visibility=private --join-policy=approval
wp jetonomy space update 5 --visibility=private
wp jetonomy space list --format=table

post

Create and manage posts within spaces.

Subcommand Description
create Create a new post
list List posts
update <id> Update a post
delete <id> Delete a post

Flags - create: --space=<id> --author=<id> --title=<title> --content=<content> [--status=<status>] [--slug=<slug>] [--format=<format>]

Status values: published (default), draft.

Flags - update: [--title=<title>] [--content=<content>] [--status=<status>] [--slug=<slug>] [--format=<format>]

wp jetonomy post create --space=5 --author=3 --title="Hello" --content="First post"
wp jetonomy post create --space=5 --author=3 --title="Draft" --content="..." --status=draft
wp jetonomy post update 42 --title="New title"
wp jetonomy post list --format=json

reply

Create and manage replies on posts.

Subcommand Description
create Create a reply
list List replies for a post
update <id> Update a reply
delete <id> Delete a reply
accept Mark a reply as the accepted answer

Flags - create: --post=<id> --author=<id> --content=<content> [--parent=<id>] [--status=<status>] [--format=<format>]

Flags - accept: --post=<id> --reply=<id> [--format=<format>]

wp jetonomy reply create --post=42 --author=3 --content="Great idea"
wp jetonomy reply create --post=42 --author=3 --content="Nested reply" --parent=17
wp jetonomy reply accept --post=42 --reply=17

tag

Manage post tags.

Subcommand Description
create Create a tag
list List all tags
update <id> Update a tag
delete <id> Delete a tag
get-by-slug Fetch a tag by its slug
attach Attach a tag to a post
detach Detach a tag from a post
list-for-post List tags on a specific post

Flags - create: --name=<name> [--format=<format>]

Flags - get-by-slug: --slug=<slug> [--format=<format>]

Flags - attach / detach: --post=<id> --tag=<id> [--format=<format>]

Flags - list-for-post: --post=<id> [--format=<format>] [--fields=<fields>]

wp jetonomy tag create --name="announcement"
wp jetonomy tag attach --post=42 --tag=7
wp jetonomy tag detach --post=42 --tag=7
wp jetonomy tag list-for-post --post=42

user

Manage community users - ban/unban, trust levels, profiles, and reputation.

Subcommand Description
create Create a new WordPress + Jetonomy user
ban <id> Ban a user site-wide
unban <id> Remove a site-wide ban
promote <id> Increase a user's trust level by one step
demote <id> Decrease a user's trust level by one step
set-trust <id> Set a user's trust level to an exact value (0-5)
get-trust <id> Read a user's current trust level
update-profile <id> Update display name, bio, or avatar URL
adjust-reputation <id> Add or subtract reputation points

Flags - create: --login=<login> --email=<email> [--password=<pw>] [--role=<role>] [--trust-level=<0-5>] [--display-name=<name>] [--format=<format>]

Flags - set-trust: --level=<0-5> [--format=<format>]

Flags - update-profile: [--display-name=<name>] [--bio=<bio>] [--avatar-url=<url>] [--format=<format>]

Flags - adjust-reputation: --delta=<int> (use negative values to subtract) [--format=<format>]

wp jetonomy user create --login=alice --email=alice@example.com
wp jetonomy user create --login=mod1 --email=m1@ex.com --trust-level=4 --role=editor
wp jetonomy user set-trust 42 --level=3
wp jetonomy user adjust-reputation 42 --delta=25
wp jetonomy user adjust-reputation 42 --delta=-10
wp jetonomy user update-profile 42 --bio="Hello world" --avatar-url="https://example.com/a.png"

member

Manage space membership - join, leave, and role assignment.

Subcommand Description
add Add a user to a space (admin shortcut)
remove Remove a user from a space
promote Promote a member to moderator
demote Demote a member from moderator
join Record a user joining a space (respects join policy)
leave Record a user leaving a space
set-role Set a member's role directly
is-member Check whether a user is a member

Note: --by is used for the user ID in all subcommands because --user is a reserved WP-CLI global flag.

Flags - join: --space=<id> --by=<user_id> [--role=<role>] [--format=<format>]

Flags - set-role: --space=<id> --by=<user_id> --role=<role> [--format=<format>]

Flags - is-member: --space=<id> --by=<user_id> [--format=<format>]

wp jetonomy member join --space=15 --by=4
wp jetonomy member join --space=15 --by=4 --role=moderator
wp jetonomy member set-role --space=15 --by=4 --role=moderator
wp jetonomy member leave --space=15 --by=4
wp jetonomy member is-member --space=15 --by=4

vote

Cast and inspect votes on posts and replies.

Subcommand Description
create Cast a vote (upvote or downvote)
delete Retract a vote
list List votes on a post or reply
cast Alias for create with explicit flags

Note: --voter is used for the voter's user ID because --user is a reserved WP-CLI global flag.

Flags - cast: --voter=<id> --type=<type> --id=<id> --value=<value> [--format=<format>]

Type: post or reply. Value: 1 (upvote) or -1 (downvote).

wp jetonomy vote cast --voter=3 --type=post --id=42 --value=1
wp jetonomy vote cast --voter=3 --type=reply --id=17 --value=-1

flag

Create and manage content flags (user reports).

Subcommand Description
list List flags (filterable by status)
resolve Resolve or dismiss a flag
create File a new flag against a post or reply

Note: --reporter is used for the reporting user ID because --user is reserved.

Flags - create: --type=<type> --id=<id> --reporter=<id> --reason=<reason> [--description=<description>] [--format=<format>]

Type: post or reply. Reason values: spam, harassment, off-topic, other.

wp jetonomy flag create --type=post --id=42 --reporter=3 --reason=spam
wp jetonomy flag create --type=reply --id=17 --reporter=3 --reason=harassment --description="Context here"
wp jetonomy flag list --format=json

notification

Trigger and inspect notifications.

Subcommand Description
send Trigger a notification event
list List notifications for a user
trigger Alias for send with explicit flags
mark-read Mark notifications as read for a user

Note: --to is used for the recipient user ID because --user is reserved.

Flags - trigger: --type=<type> --to=<user_id> --actor=<user_id> --object-type=<type> --object-id=<id> --message=<text> [--format=<format>]

Flags - list: --to=<user_id> [--limit=<n>] [--offset=<n>] [--fields=<fields>] [--format=<format>]

wp jetonomy notification trigger --type=reply_to_post --to=1 --actor=2 --object-type=post --object-id=5 --message="Someone replied"
wp jetonomy notification list --to=1
wp jetonomy notification list --to=1 --limit=5 --format=json

config

Read and write Jetonomy settings using dotted key paths.

Subcommand Description
get Read a setting value
set Write a setting value
list List all settings
reset Reset a single key to its default
reset-all Reset all settings to defaults
keys List available keys under a parent path

Flags - get: [--key=<dotted_path>] [--format=<format>]

Flags - set: --key=<dotted_path> --value=<value> [--format=<format>]

Flags - keys: [--key=<parent_path>] [--format=<format>] [--fields=<fields>]

wp jetonomy config get --key=trust_thresholds.1.posts
wp jetonomy config get --key=rate_limits --format=json
wp jetonomy config set --key=trust_thresholds.1.posts --value=7
wp jetonomy config set --key=notification_defaults.mention.email --value=false
wp jetonomy config keys --key=trust_thresholds
wp jetonomy config list

mod

Advanced moderation actions: bans, flag management, and content decisions.

Subcommand Description
approve Approve flagged content
spam Mark content as spam
trash Trash content
flags List flags with optional status filter
resolve Resolve or dismiss a flag
ban Ban a user (site-wide or space-scoped)
unban Lift a ban
is-banned Check whether a user is banned

Note: --target is used for the affected user and --issuer for the moderator, avoiding the reserved --user flag.

Flags - flags: [--status=<status>] [--format=<format>] [--fields=<fields>]

Status values: valid, dismissed.

Flags - resolve: --resolver=<user_id> --decision=<decision> [--format=<format>]

Flags - ban: --target=<user_id> --issuer=<user_id> [--type=<type>] [--space=<id>] [--reason=<text>] [--expires=<datetime>] [--format=<format>]

Flags - is-banned: --target=<user_id> [--space=<id>] [--format=<format>]

wp jetonomy mod flags --status=valid
wp jetonomy mod resolve 42 --resolver=1 --decision=valid
wp jetonomy mod resolve 17 --resolver=1 --decision=dismissed
wp jetonomy mod ban --target=5 --issuer=1 --reason="spam"
wp jetonomy mod ban --target=5 --issuer=1 --type=space_ban --space=3
wp jetonomy mod ban --target=5 --issuer=1 --type=silence --expires="2026-05-01 00:00:00"
wp jetonomy mod unban 5
wp jetonomy mod is-banned --target=5 --space=3

scenario

Run end-to-end PHP scenarios that exercise full user journeys against the live database.

Subcommand Description
run <name> Run a named scenario
list List all available scenarios

Flags - run: [--cleanup] (roll back all data created by the scenario) [--format=<format>]

Flags - list: [--format=<format>] [--fields=<fields>]

Bundled scenarios: basic-forum-flow, notification-delivery-sweep, multi-user-voting-thread, moderation-lifecycle, space-member-journey.

wp jetonomy scenario list
wp jetonomy scenario list --format=json
wp jetonomy scenario run basic-forum-flow
wp jetonomy scenario run notification-delivery-sweep --cleanup
wp jetonomy scenario run multi-user-voting-thread --format=json

Pro Commands

Pro commands use wp jetonomy-pro as the top-level namespace. Every Pro subcommand requires both Jetonomy (free) and Jetonomy Pro to be active.


extension

Enable, disable, and inspect Pro extensions.

Subcommand Description
list List all extensions and their current state
enable <id> Enable an extension
disable <id> Disable an extension
activate <id> Activate (boot) an extension that is already enabled
deactivate <id> Deactivate a running extension
status <id> Show status for a single extension
wp jetonomy-pro extension list
wp jetonomy-pro extension list --format=json
wp jetonomy-pro extension enable private-messaging
wp jetonomy-pro extension disable polls
wp jetonomy-pro extension status webhooks

custom-fields

Manage custom fields that extend post or user objects.

Subcommand Description
list List all defined custom fields
create Define a new custom field
delete <id> Remove a custom field definition

Flags - create: --key=<slug> --label=<text> --type=<type> --applies-to=<target> [--description=<text>] [--options=<csv>] [--required] [--default=<value>] [--format=<format>]

Types: text, textarea, select, checkbox, number, url. Applies-to: post or user.

wp jetonomy-pro custom-fields create --key=company --label=Company --type=text --applies-to=user
wp jetonomy-pro custom-fields create --key=priority --label=Priority --type=select --applies-to=post --options="low,medium,high"
wp jetonomy-pro custom-fields list --format=json
wp jetonomy-pro custom-fields delete 4

white-label

Set white-label branding for the community frontend.

Subcommand Description
set-logo Replace the community logo
set-colors Set primary and accent brand colors
wp jetonomy-pro white-label set-logo --url=https://example.com/logo.png
wp jetonomy-pro white-label set-colors --primary="#1a73e8" --accent="#fbbc04"

reactions

Manage emoji reactions on posts and replies.

Subcommand Description
list List all reactions on an object
purge Remove all reactions from an object
add Add a reaction from a user
remove Remove a user's reaction

Note: --by is used for the acting user, --on-type and --on-id for the target object.

Flags - add / remove: --on-type=<type> --on-id=<id> --by=<user_id> --emoji=<slug> [--format=<format>]

wp jetonomy-pro reactions add --on-type=post --on-id=12 --by=1 --emoji=thumbsup
wp jetonomy-pro reactions add --on-type=reply --on-id=45 --by=2 --emoji=heart
wp jetonomy-pro reactions list --on-type=post --on-id=12
wp jetonomy-pro reactions purge --on-type=post --on-id=12

ai

Inspect and test the AI moderation / summarization provider.

Subcommand Description
test-provider <provider> Send a test prompt to a named provider
clear-cache Purge the AI response cache
export-usage Export AI API usage records
status Show current provider health
list-providers List all configured AI providers
provider-status <provider> Show status for a specific provider

Flags - test-provider: [--prompt=<text>] [--format=<format>]

wp jetonomy-pro ai status
wp jetonomy-pro ai list-providers --format=json
wp jetonomy-pro ai test-provider anthropic --prompt="Say hi"
wp jetonomy-pro ai clear-cache
wp jetonomy-pro ai export-usage --format=json

advanced-moderation

Inspect and test automated moderation rules.

Subcommand Description
list-rules List all active moderation rules
test-rule <id> Test a rule against sample content
wp jetonomy-pro advanced-moderation list-rules
wp jetonomy-pro advanced-moderation list-rules --format=json
wp jetonomy-pro advanced-moderation test-rule 3

custom-badges

Award and inspect custom reputation badges.

Subcommand Description
list List all badge definitions
award Award a badge to a user

Flags - award: --badge=<id> --to=<user_id> [--format=<format>]

wp jetonomy-pro custom-badges list
wp jetonomy-pro custom-badges award --badge=5 --to=42

polls

Create and manage polls attached to posts.

Subcommand Description
list List polls
close <id> Close a poll early
create Create a poll on a post
get <id> Get poll details and current results
vote Cast a vote on a poll option

Note: --by is used for the voter user ID because --user is reserved.

Flags - create: --post=<id> --question=<text> --options=<csv> [--multiple] [--closes-at=<datetime>] [--format=<format>]

Flags - vote: --post=<id> --by=<user_id> and the option index/id.

wp jetonomy-pro polls create --post=12 --question="Favourite colour?" --options="Red,Green,Blue"
wp jetonomy-pro polls create --post=12 --question="Pick many" --options="A,B,C" --multiple
wp jetonomy-pro polls get 4
wp jetonomy-pro polls close 4

email-digest

Manage user email digest preferences and trigger sends.

Subcommand Description
send-now Send a digest immediately for a user
export-digests Export digest records
get-prefs Get a user's digest preferences
set-prefs Update a user's digest preferences

Note: --for is used for the target user ID because --user is reserved.

Flags - get-prefs / send-now: --for=<user_id> [--format=<format>]

Flags - set-prefs: --for=<user_id> [--frequency=<frequency>] [--enabled] [--disabled] [--types=<csv>] [--format=<format>]

Frequency values: daily, weekly, off.

wp jetonomy-pro email-digest get-prefs --for=1
wp jetonomy-pro email-digest set-prefs --for=1 --frequency=weekly --enabled
wp jetonomy-pro email-digest set-prefs --for=1 --frequency=off
wp jetonomy-pro email-digest send-now --for=1

web-push

Manage browser push subscriptions and send push notifications.

Subcommand Description
send Send a push notification to a user
list-subscriptions List a user's active push subscriptions
subscribe Register a push subscription for a user
unsubscribe Remove a push subscription

Note: --for is used for the subscriber user ID, --to for the notification recipient.

Flags - subscribe: --for=<user_id> --endpoint=<url> --p256dh=<key> --auth=<key> [--format=<format>]

Flags - unsubscribe: --for=<user_id> --endpoint=<url> [--format=<format>]

wp jetonomy-pro web-push list-subscriptions --for=1
wp jetonomy-pro web-push send --to=1 --title="New reply" --body="Alice replied to your post"

analytics

Export and report on community analytics.

Subcommand Description
export Export raw analytics data
report Print a formatted summary report
overview High-level stats for a date range
top-spaces Rank spaces by activity for a period

Flags - overview: [--range=<range>] [--start=<date>] [--end=<date>] [--format=<format>]

Range values: 7d, 30d, 90d, custom. When using custom, provide --start=YYYY-MM-DD and --end=YYYY-MM-DD.

Flags - top-spaces: [--range=<range>] [--limit=<n>] [--format=<format>]

wp jetonomy-pro analytics overview --range=7d
wp jetonomy-pro analytics overview --range=custom --start=2026-03-01 --end=2026-03-15
wp jetonomy-pro analytics top-spaces --range=30d --limit=5
wp jetonomy-pro analytics export --range=30d --format=csv

seo-pro

Generate SEO sitemaps for community content.

Subcommand Description
generate-sitemaps Rebuild all Jetonomy sitemaps
wp jetonomy-pro seo-pro generate-sitemaps

webhooks

Manage outbound webhook endpoints.

Subcommand Description
list List all registered webhooks
test <id> Send a test ping to a webhook
retry <id> Retry failed deliveries for a webhook
create Register a new webhook
update <id> Update webhook settings

Note: --target-url is used instead of --url because WP-CLI reserves --url globally for multisite routing.

Flags - create: --target-url=<url> --events=<csv> [--name=<text>] [--secret=<text>] [--description=<text>] [--disabled] [--format=<format>]

Secret is auto-generated if omitted. Events are a comma-separated list of event slugs such as post.created, reply.created, user.registered.

Flags - update: [--target-url=<url>] [--events=<csv>] [--name=<text>] [--description=<text>] [--format=<format>]

wp jetonomy-pro webhooks list
wp jetonomy-pro webhooks create --target-url=https://example.com/hook --events=post.created,reply.created
wp jetonomy-pro webhooks create --target-url=https://example.com/hook --events=user.registered --disabled
wp jetonomy-pro webhooks test 3
wp jetonomy-pro webhooks retry 3

messaging

Manage private message conversations.

Subcommand Description
export-conversations Export conversation records for a user
purge-old Delete conversations older than a given age
create-conversation Start a new private conversation
send Send a message in an existing conversation

Note: --by is the conversation initiator, --with is a comma-separated list of participant IDs, --from is the message sender.

Flags - create-conversation: --by=<user_id> --with=<csv> [--title=<text>] [--type=<type>] [--message=<text>] [--format=<format>]

Flags - send: --conversation=<id> --from=<user_id> --content=<text> [--format=<format>]

wp jetonomy-pro messaging create-conversation --by=1 --with=3,4
wp jetonomy-pro messaging create-conversation --by=1 --with=2 --message="Hi there"
wp jetonomy-pro messaging send --conversation=12 --from=1 --content="hello"
wp jetonomy-pro messaging export-conversations --by=1

reply-by-email

Configure and test inbound reply-by-email processing.

Subcommand Description
configure Set SMTP / IMAP connection settings
test-smtp Send a test email to verify outbound settings
wp jetonomy-pro reply-by-email configure --host=mail.example.com --user=inbox@example.com
wp jetonomy-pro reply-by-email test-smtp

Testing and QA Commands

These commands run the built-in quality assurance suites against a live WordPress + Jetonomy install. Run them before every release to catch regressions early.

qa-actions (free)

Runs all four smoke-test phases in sequence and reports a pass/fail total. Takes no arguments.

Phase What it covers
Phase 1 REST round-trip tests (creates, reads, deletes via the REST API)
Phase 2 Model unit tests (direct model-layer assertions)
Phase 3 Pro extension tests (skipped automatically on free-only installs)
Phase 4 Journey smoke tests (C1-C12 - full end-to-end user journeys)
wp jetonomy qa-actions

Expected output when all tests pass:

--- Jetonomy Action Tests ---

Phase 1: REST Round-Trip Tests
...
Phase 4: Journey Smoke Tests (C1-C12)
...
  REST Tests:     42/42
  Model Tests:    58/58
  Pro Tests:      64/64
  Journey Tests:  46/46
  Total: 210/210 PASS

scenario run (free)

Run a named end-to-end scenario against the live database. Use --cleanup to roll back all data the scenario creates.

# List available scenarios first
wp jetonomy scenario list

# Run a scenario and keep the data (useful for manual inspection after)
wp jetonomy scenario run basic-forum-flow

# Run and clean up all created data
wp jetonomy scenario run notification-delivery-sweep --cleanup
wp jetonomy scenario run moderation-lifecycle --cleanup

Scenarios are PHP classes under includes/cli/scenarios/ and can be run directly by PHPUnit via composer test:combo if you prefer not to use a live database.

Something unclear? Open a support ticket →

Buy Jetonomy