API: mobieusBuddy & mobieusChallenge
Peer accountability and competitive challenges, both of which move health data between members — so both are consent-gated and audited. Documented in the REST API from 1.23.0.
These surfaces were reachable before 1.23.0 but enforced no scope. If you built against them earlier, your key now needs the scopes below.
Plan and feature gates
Available on Pro, Creator, Creator Plus, and Sovereign. Each product is also behind its own feature flag — mobieus_buddy and mobieus_challenge. When a flag is off, its routes return 404, not 403: a disabled feature is invisible rather than advertised.
Scopes
Scope follows the data, not the product. Anything that reads or governs access to a health record takes a health:* scope; social actions that carry a message rather than a health value take social:*.
| Scope | Used for |
|---|---|
health:read |
The buddy graph, pacts, challenge leaderboards, and reading a buddy's shared data |
health:write |
Creating or revoking a sharing grant — invite, accept, withdraw, pause, resume, set-grants, join a challenge, leave one |
social:read |
Challenge and buddy metadata, feeds, invitations |
social:write |
Nudges, cheers, comments, reactions, flags |
mobieusBuddy
| Method | Path | Scope |
|---|---|---|
GET |
/api/v1/buddy |
health:read |
POST |
/api/v1/buddy/invite |
health:write |
POST |
/api/v1/buddy/{id}/accept |
health:write |
POST |
/api/v1/buddy/{id}/withdraw |
health:write |
POST |
/api/v1/buddy/{id}/pause · /resume |
health:write |
POST |
/api/v1/buddy/{id}/grants |
health:write |
POST |
/api/v1/buddy/{id}/prefs |
health:write |
GET |
/api/v1/buddy/{ownerId}/data/{category} |
health:read |
GET POST |
/api/v1/buddy/interactions |
social:read / social:write |
GET |
/api/v1/buddy/pacts · POST /api/v1/buddy/{id}/pacts |
health:read / health:write |
Reading a buddy's data
curl https://YOUR-TENANT.mobieus.io/api/v1/buddy/42/data/weight \
-H "Authorization: Bearer mc_live_..."
The owner must have granted you that category. Without a grant you get 403 not_shared — the same response you get for a member who never shared with you, so the endpoint does not disclose whether a relationship exists.
Every read is written to the owner's access log. They can see who looked, at what, and when, and they can revoke at any time; revocation takes effect on the next call.
The response is shaped to the detail level the owner chose, not to what you asked for: full returns entries, summary returns a rollup without absolute values, streak returns consistency only. Do not assume entries are present.
mobieusChallenge
| Method | Path | Scope |
|---|---|---|
GET POST |
/api/v1/challenges |
social:read / social:write |
GET |
/api/v1/challenges/{id} |
social:read |
POST |
/api/v1/challenges/{id}/join · /leave · /join-by-code |
health:write |
GET |
/api/v1/challenges/{id}/leaderboard |
health:read |
GET POST |
/api/v1/challenges/{id}/feed |
social:read / social:write |
POST |
/api/v1/challenges/{id}/feed/{postId}/react · /delete |
social:write |
POST |
/api/v1/challenges/{id}/invite · /invite/decline |
social:write |
GET POST |
/api/v1/challenges/{id}/flags · /flag |
social:read / social:write |
Joining a challenge is a health:write action because it creates a sharing grant: your scored metric becomes visible to that challenge's members for its duration. Leaving revokes it.
Leaderboards read from server-computed snapshots of the scored metric only — never raw logs. A percentage challenge stores the percentage, not the weight behind it.
403 not_participant on a leaderboard or feed means the key owner has not joined that challenge. It is not a scope problem.
See also
## API: mobieusBuddy & mobieusChallenge
Peer accountability and competitive challenges, both of which move health data between members — so both are consent-gated and audited. Documented in the REST API from **1.23.0**.
These surfaces were reachable before 1.23.0 but enforced no scope. If you built against them earlier, your key now needs the scopes below.
### Plan and feature gates
Available on **Pro**, **Creator**, **Creator Plus**, and **Sovereign**. Each product is also behind its own feature flag — `mobieus_buddy` and `mobieus_challenge`. When a flag is off, its routes return `404`, not `403`: a disabled feature is invisible rather than advertised.
### Scopes
Scope follows the **data**, not the product. Anything that reads or governs access to a health record takes a `health:*` scope; social actions that carry a message rather than a health value take `social:*`.
| Scope | Used for |
|---|---|
| `health:read` | The buddy graph, pacts, challenge leaderboards, and reading a buddy's shared data |
| `health:write` | Creating or revoking a sharing grant — invite, accept, withdraw, pause, resume, set-grants, join a challenge, leave one |
| `social:read` | Challenge and buddy metadata, feeds, invitations |
| `social:write` | Nudges, cheers, comments, reactions, flags |
### mobieusBuddy
| Method | Path | Scope |
|---|---|---|
| `GET` | `/api/v1/buddy` | `health:read` |
| `POST` | `/api/v1/buddy/invite` | `health:write` |
| `POST` | `/api/v1/buddy/{id}/accept` | `health:write` |
| `POST` | `/api/v1/buddy/{id}/withdraw` | `health:write` |
| `POST` | `/api/v1/buddy/{id}/pause` · `/resume` | `health:write` |
| `POST` | `/api/v1/buddy/{id}/grants` | `health:write` |
| `POST` | `/api/v1/buddy/{id}/prefs` | `health:write` |
| `GET` | `/api/v1/buddy/{ownerId}/data/{category}` | `health:read` |
| `GET` `POST` | `/api/v1/buddy/interactions` | `social:read` / `social:write` |
| `GET` | `/api/v1/buddy/pacts` · `POST /api/v1/buddy/{id}/pacts` | `health:read` / `health:write` |
#### Reading a buddy's data
```bash
curl https://YOUR-TENANT.mobieus.io/api/v1/buddy/42/data/weight \
-H "Authorization: Bearer mc_live_..."
```
The owner must have granted you that category. Without a grant you get `403 not_shared` — the same response you get for a member who never shared with you, so the endpoint does not disclose whether a relationship exists.
Every read is written to the owner's access log. They can see who looked, at what, and when, and they can revoke at any time; revocation takes effect on the next call.
The response is shaped to the **detail level** the owner chose, not to what you asked for: `full` returns entries, `summary` returns a rollup without absolute values, `streak` returns consistency only. Do not assume entries are present.
### mobieusChallenge
| Method | Path | Scope |
|---|---|---|
| `GET` `POST` | `/api/v1/challenges` | `social:read` / `social:write` |
| `GET` | `/api/v1/challenges/{id}` | `social:read` |
| `POST` | `/api/v1/challenges/{id}/join` · `/leave` · `/join-by-code` | `health:write` |
| `GET` | `/api/v1/challenges/{id}/leaderboard` | `health:read` |
| `GET` `POST` | `/api/v1/challenges/{id}/feed` | `social:read` / `social:write` |
| `POST` | `/api/v1/challenges/{id}/feed/{postId}/react` · `/delete` | `social:write` |
| `POST` | `/api/v1/challenges/{id}/invite` · `/invite/decline` | `social:write` |
| `GET` `POST` | `/api/v1/challenges/{id}/flags` · `/flag` | `social:read` / `social:write` |
Joining a challenge is a `health:write` action because it **creates a sharing grant**: your scored metric becomes visible to that challenge's members for its duration. Leaving revokes it.
Leaderboards read from server-computed snapshots of the scored metric only — never raw logs. A percentage challenge stores the percentage, not the weight behind it.
`403 not_participant` on a leaderboard or feed means the key owner has not joined that challenge. It is not a scope problem.
### See also
- [mobieusCore API — Overview](/know/api-overview)
- [mobieusCore API — Authentication, scopes & rate limits](/know/api-authentication)
- [mobieusCore API — mobieusHealth](/know/api-health)