REST API
The plugin extends the BuddyPress REST namespace
(/wp-json/buddypress/v1/, or the BuddyBoss Platform equivalent when BB
Platform is active) with sticky-post endpoints, and augments existing
activity responses with sticky metadata.
All custom endpoints live in includes/class-bpsp-rest-api.php. The
gamification stats endpoint lives in includes/bpsp-gamification-hooks.php.
Namespace resolution
Section titled “Namespace resolution”The plugin detects which namespace to use at runtime:
| Platform | Namespace |
|---|---|
| BuddyPress (default) | buddypress/v1 |
| BuddyBoss Platform | Result of bp_rest_namespace() . '/' . bp_rest_version() |
Older BP with BP_REST_NAMESPACE constant |
{BP_REST_NAMESPACE}/v1 |
In practice, BuddyBoss Platform typically resolves to buddyboss/v1.
Replace buddypress/v1 with the correct namespace for your install.
Endpoints overview
Section titled “Endpoints overview”| Method | Path | Auth | Purpose |
|---|---|---|---|
GET |
/buddypress/v1/activity/{id}/sticky |
Public | Sticky status of a single activity |
POST |
/buddypress/v1/activity/{id}/sticky |
Logged-in + pin permission | Pin the activity |
DELETE |
/buddypress/v1/activity/{id}/sticky |
Logged-in + unpin permission | Unpin the activity |
GET |
/buddypress/v1/activity/sticky |
Public | List all sticky activity IDs + objects |
GET |
/bpsp/v1/users/{id}/stats |
Logged-in (read cap) |
Per-user pin statistics |
POST and DELETE enforce the same per-user / per-admin pin quota as
every other pin entry point — REST clients cannot bypass
max_user_sticky_posts or max_admin_sticky_posts.
GET sticky status
Section titled “GET sticky status”curl -s 'https://example.com/wp-json/buddypress/v1/activity/123/sticky'Response when pinned:
{ "id": 123, "is_sticky": true, "sticky_data": { "pinned_date": "2025-01-01T12:00:00", "can_unpin": true }}Response when not pinned:
{ "id": 124, "is_sticky": false, "can_pin": true}pinned_by (user ID) is added inside sticky_data only when the
requester holds the bp_moderate capability.
POST — pin an activity
Section titled “POST — pin an activity”curl -X POST \ -H 'X-WP-Nonce: <nonce>' \ 'https://example.com/wp-json/buddypress/v1/activity/123/sticky'Success response (200):
{ "id": 123, "is_sticky": true, "message": "Activity pinned successfully." }The endpoint determines the pin scope from the activity itself: activities
with component = 'groups' pin into that group’s scope; everything else
pins as activity. Users holding bp_moderate pin as 'administrator'
(sitewide admin quota applies); group admins acting inside their own group
bypass the per-user quota.
Error responses:
| Code | HTTP status | Cause |
|---|---|---|
bp_rest_authorization_required |
401 |
Not logged in |
bp_rest_invalid_id |
404 |
Activity does not exist |
bp_rest_already_pinned |
400 |
Activity already pinned |
bp_rest_authorization_required |
403 |
Logged in but no pin permission |
bpsp_quota_exceeded |
429 |
Per-user or per-admin pin cap reached |
bp_rest_pin_failed |
500 |
DB write failed |
DELETE — unpin an activity
Section titled “DELETE — unpin an activity”curl -X DELETE \ -H 'X-WP-Nonce: <nonce>' \ 'https://example.com/wp-json/buddypress/v1/activity/123/sticky'Success response (200):
{ "id": 123, "is_sticky": false, "message": "Activity unpinned successfully." }The DELETE permission check is more permissive than POST: the user who
originally pinned the activity can always remove their own pin, even if
their role or group membership has since changed. This prevents “stuck”
pins when permission rules tighten between plugin updates.
GET sticky list
Section titled “GET sticky list”# Sitewide sticky activitiescurl -s 'https://example.com/wp-json/buddypress/v1/activity/sticky'
# Group-specific sticky activitiescurl -s 'https://example.com/wp-json/buddypress/v1/activity/sticky?component=groups&group_id=42'Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
component |
string | activity |
activity or groups |
group_id |
integer | 0 |
Required when component=groups |
Response shape:
{ "activities": [ /* full activity objects */ ], "total": 3}Each activity object passes through BP_REST_Activity_Endpoint::prepare_item_for_response()
so the shape matches every other BuddyPress activity response.
GET user stats
Section titled “GET user stats”curl -s \ -H 'X-WP-Nonce: <nonce>' \ 'https://example.com/wp-json/bpsp/v1/users/5/stats'This endpoint uses its own namespace (bpsp/v1) regardless of whether
BuddyBoss Platform is active.
Permission: current_user_can( 'read' ) — any logged-in user.
Response:
{ "user_id": 5, "total_pins": 14, "current_pins": 2, "group_pins": 1}| Field | Description |
|---|---|
total_pins |
Lifetime count of pin actions (from activity log) |
current_pins |
Currently active personal-stream pins |
group_pins |
Currently active group-stream pins |
Augmented activity responses
Section titled “Augmented activity responses”The plugin hooks into the standard /buddypress/v1/activity and
/buddypress/v1/activity/{id} responses via
bp_rest_activity_prepare_value / bp_rest_activity_prepare_item and
adds three optional fields:
| Field | Present when | Type | Description |
|---|---|---|---|
is_sticky |
Always | boolean |
Whether the activity is currently pinned |
sticky_data |
When is_sticky: true |
object |
{ pinned_date, can_unpin, pinned_by? } |
can_pin |
When is_sticky: false |
boolean |
Whether the requester can pin this activity |
pinned_by inside sticky_data is only included for requesters with
bp_moderate capability.
These additions are declared in the activity schema via
bp_rest_activity_schema, so REST client SDKs that read the schema will
discover them automatically.
JavaScript fetch example
Section titled “JavaScript fetch example”The plugin’s own frontend uses wp.apiFetch from wp-api-fetch:
// Pin an activitywp.apiFetch( { path: '/buddypress/v1/activity/123/sticky', method: 'POST',} ).then( ( response ) => { console.log( response.message ); // "Activity pinned successfully."} );Related
Section titled “Related”- Hooks & Filters — events fired by REST writes.
- WP-CLI — same operations from the shell.

