|
| 1 | +# Nextcloud Social API Reference |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Social app provides three categories of APIs: |
| 6 | + |
| 7 | +- **Mastodon-compatible REST API** under `/api/v1/` — designed to be compatible with the Mastodon client API specification |
| 8 | +- **Custom Local API** under `/api/v1/` and `/local/v1/` — app-specific endpoints for the Vue frontend |
| 9 | +- **ActivityPub Federation API** under `/users/`, `/@{username}/`, and `/inbox` — W3C ActivityPub protocol endpoints |
| 10 | + |
| 11 | +Base URL: `/apps/social` (e.g. `https://cloud.example/apps/social/api/v1/statuses`) |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Authentication |
| 16 | + |
| 17 | +The API supports two authentication methods: |
| 18 | + |
| 19 | +1. **OAuth Bearer Token** — obtained via the OAuth flow (see OAuth section below). Include as `Authorization: Bearer <token>` header. |
| 20 | +2. **Session-based** — uses the Nextcloud session cookie when the user is logged in to the web UI. |
| 21 | + |
| 22 | +Public endpoints (ActivityPub, media serving) do not require authentication. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Mastodon-compatible API |
| 27 | + |
| 28 | +### Instance |
| 29 | + |
| 30 | +| Method | Route | Description | |
| 31 | +|--------|-------|-------------| |
| 32 | +| GET | `/api/v1/instance/` | Returns local instance metadata (title, version, usage stats, registration) | |
| 33 | +| GET | `/api/v1/apps/verify_credentials` | Returns connected app name and website | |
| 34 | +| GET | `/api/v1/custom_emojis` | Returns custom emoji list (currently empty, not implemented) | |
| 35 | + |
| 36 | +### Accounts |
| 37 | + |
| 38 | +| Method | Route | Parameters | Description | |
| 39 | +|--------|-------|------------|-------------| |
| 40 | +| GET | `/api/v1/accounts/verify_credentials` | — | Returns the authenticated user's account | |
| 41 | +| GET | `/api/v1/accounts/relationships` | `id[]` (account IDs) | Returns relationship info (following, followed_by, etc.) | |
| 42 | +| GET | `/api/v1/accounts/{account}/statuses` | `limit`, `max_id`, `min_id`, `since_id` | Returns statuses by a specific account | |
| 43 | +| GET | `/api/v1/accounts/{account}/following` | `limit`, `max_id`, `min_id` | Returns who an account is following | |
| 44 | +| GET | `/api/v1/accounts/{account}/followers` | `limit`, `max_id`, `min_id` | Returns an account's followers | |
| 45 | + |
| 46 | +### Statuses |
| 47 | + |
| 48 | +| Method | Route | Parameters | Description | |
| 49 | +|--------|-------|------------|-------------| |
| 50 | +| POST | `/api/v1/statuses` | `status`, `visibility`, `media_ids[]`, `in_reply_to_id`, `spoiler_text`, `sensitive` | Create a new post | |
| 51 | +| GET | `/api/v1/statuses/{nid}` | — | Get a single status by NID | |
| 52 | +| PUT | `/api/v1/statuses/{nid}` | `status`, `spoiler_text`, `sensitive` | Edit an existing post | |
| 53 | +| GET | `/api/v1/statuses/{nid}/context` | — | Get conversation context (ancestors and descendants) | |
| 54 | +| POST | `/api/v1/statuses/{nid}/{act}` | `act`: `favourite`, `unfavourite`, `boost`, `unboost` | Perform an action on a status | |
| 55 | + |
| 56 | +### Timelines |
| 57 | + |
| 58 | +| Method | Route | Parameters | Description | |
| 59 | +|--------|-------|------------|-------------| |
| 60 | +| GET | `/api/v1/timelines/{timeline}/` | `local`, `limit` (20), `max_id`, `min_id`, `since_id` | Get a timeline. `timeline` values: `home`, `account`, `public`, `direct`, `favourites` | |
| 61 | +| GET | `/api/v1/timelines/tag/{hashtag}` | `limit` (20), `max_id`, `min_id`, `since_id`, `local`, `only_media` | Get posts with a specific hashtag | |
| 62 | +| GET | `/api/v1/favourites/` | `limit` (20), `max_id`, `min_id`, `since_id` | Get liked/favourited posts | |
| 63 | + |
| 64 | +### Notifications |
| 65 | + |
| 66 | +| Method | Route | Parameters | Description | |
| 67 | +|--------|-------|------------|-------------| |
| 68 | +| GET | `/api/v1/notifications` | `limit` (20), `max_id`, `min_id`, `since_id`, `types[]`, `exclude_types[]`, `accountId` | Get notifications for the viewer | |
| 69 | + |
| 70 | +### Media |
| 71 | + |
| 72 | +| Method | Route | Parameters | Description | |
| 73 | +|--------|-------|------------|-------------| |
| 74 | +| POST | `/api/v1/media` | `file` (multipart upload) | Upload a media attachment | |
| 75 | +| GET | `/api/v1/media/{nid}` | `preview` (optional) | Get media metadata by NID (stub, not fully implemented) | |
| 76 | +| GET | `/media/{uuid}` | — | Serve a media file by UUID (supports optional `.ext` suffix) | |
| 77 | + |
| 78 | +### Search |
| 79 | + |
| 80 | +| Method | Route | Parameters | Description | |
| 81 | +|--------|-------|------------|-------------| |
| 82 | +| GET | `/api/v1/search` | `search` (query string) | Unified search across accounts, hashtags, and post content | |
| 83 | +| GET | `/api/v1/global/accounts/search` | `search` | Search cached accounts by name/identifier | |
| 84 | +| GET | `/api/v1/global/tags/search` | `search` | Search hashtags | |
| 85 | + |
| 86 | +### Pagination |
| 87 | + |
| 88 | +Endpoints that return lists support cursor-based pagination via these query parameters: |
| 89 | + |
| 90 | +- `limit` — maximum number of items to return (default: 20) |
| 91 | +- `max_id` — return items older than this ID |
| 92 | +- `min_id` — return items newer than this ID |
| 93 | +- `since_id` — return items created after this ID |
| 94 | +- `since` — return items published after this Unix timestamp |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## Custom Local API |
| 99 | + |
| 100 | +### Streams |
| 101 | + |
| 102 | +| Method | Route | Parameters | Description | |
| 103 | +|--------|-------|------------|-------------| |
| 104 | +| GET | `/api/v1/stream/home` | `since`, `limit` | Home timeline (posts from followed accounts) | |
| 105 | +| GET | `/api/v1/stream/notifications` | `since`, `limit` | User notifications | |
| 106 | +| GET | `/api/v1/stream/timeline` | `since`, `limit` | Local timeline (all local public posts) | |
| 107 | +| GET | `/api/v1/stream/federated` | `since`, `limit` | Global/federated timeline | |
| 108 | +| GET | `/api/v1/stream/direct` | `since`, `limit` | Direct messages | |
| 109 | +| GET | `/api/v1/stream/liked` | `since`, `limit` | Liked posts | |
| 110 | +| GET | `/api/v1/stream/tag/{hashtag}/` | `since`, `limit` | Posts by hashtag | |
| 111 | +| GET | `/api/v1/account/{username}/stream` | `since`, `limit` | Posts by a specific account | |
| 112 | + |
| 113 | +### Posts |
| 114 | + |
| 115 | +| Method | Route | Parameters | Description | |
| 116 | +|--------|-------|------------|-------------| |
| 117 | +| GET | `/local/v1/post` | `id` (ActivityPub ID) | Get a single post by its ActivityPub ID | |
| 118 | +| GET | `/local/v1/post/replies` | `id`, `since`, `limit` | Get replies to a post | |
| 119 | +| POST | `/api/v1/post` | `content`, `to[]`, `type`, `replyTo`, `attachments`, `hashtags[]` | Create a new post | |
| 120 | +| DELETE | `/api/v1/post` | `id` | Delete own post | |
| 121 | +| POST | `/api/v1/post/like` | `postId` | Like a post | |
| 122 | +| DELETE | `/api/v1/post/like` | `postId` | Unlike a post | |
| 123 | + |
| 124 | +### Current User |
| 125 | + |
| 126 | +| Method | Route | Description | |
| 127 | +|--------|-------|-------------| |
| 128 | +| GET | `/api/v1/current/info` | Get current user's actor info with complete details | |
| 129 | +| GET | `/api/v1/current/followers` | Get current user's followers | |
| 130 | +| GET | `/api/v1/current/following` | Get who the current user follows | |
| 131 | +| PUT | `/api/v1/current/follow` | Follow an account (`?account=`) | |
| 132 | +| DELETE | `/api/v1/current/follow` | Unfollow an account (`?account=`) | |
| 133 | + |
| 134 | +### Account Info |
| 135 | + |
| 136 | +| Method | Route | Parameters | Description | |
| 137 | +|--------|-------|------------|-------------| |
| 138 | +| GET | `/api/v1/account/{username}/info` | — | Get info for a local account (with cache fallback) | |
| 139 | +| GET | `/api/v1/global/account/info` | `account` (e.g. `@user@domain`) | Get info for any local or remote account | |
| 140 | +| GET | `/api/v1/global/actor/info` | `id` (ActivityPub actor ID) | Get actor info by raw ActivityPub ID | |
| 141 | +| GET | `/api/v1/global/actor/avatar` | `id` | Serve an actor's avatar image | |
| 142 | +| GET | `/api/v1/global/actor/header` | `id` | Redirect to an actor's banner/header image | |
| 143 | + |
| 144 | +### Banner |
| 145 | + |
| 146 | +| Method | Route | Parameters | Description | |
| 147 | +|--------|-------|------------|-------------| |
| 148 | +| POST | `/api/v1/banner` | `file` (multipart) | Upload a profile banner image | |
| 149 | +| POST | `/api/v1/banner/url` | `url` | Download and set a profile banner from a URL | |
| 150 | + |
| 151 | +### Config |
| 152 | + |
| 153 | +| Method | Route | Parameters | Description | |
| 154 | +|--------|-------|------------|-------------| |
| 155 | +| POST | `/api/v1/config/cloudAddress` | `cloudAddress` | Set the cloud base URL in app config | |
| 156 | + |
| 157 | +### System |
| 158 | + |
| 159 | +| Method | Route | Description | |
| 160 | +|--------|-------|-------------| |
| 161 | +| GET | `/local/` | Get app version and setup status | |
| 162 | +| GET | `/test/{account}/` | Run a WebFinger test against an account (debug only, requires `social.tests` system config) | |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## OAuth |
| 167 | + |
| 168 | +The Social app implements a Mastodon-compatible OAuth flow. |
| 169 | + |
| 170 | +| Method | Route | Parameters | Description | |
| 171 | +|--------|-------|------------|-------------| |
| 172 | +| POST | `/api/v1/apps` | `client_name`, `redirect_uris`, `website`, `scopes` | Register a new OAuth client app | |
| 173 | +| GET | `/oauth/authorize` | `client_id`, `redirect_uri`, `response_type`, `scope` | Show OAuth authorization page | |
| 174 | +| POST | `/oauth/authorize` | `client_id`, `redirect_uri`, `response_type`, `scope` | Confirm OAuth authorization | |
| 175 | +| POST | `/oauth/token` | `client_id`, `client_secret`, `redirect_uri`, `grant_type`, `code` | Exchange authorization code for bearer token | |
| 176 | + |
| 177 | +**Grant types:** `authorization_code`, `client_credentials` |
| 178 | + |
| 179 | +**Default scope:** `read` |
| 180 | + |
| 181 | +--- |
| 182 | + |
| 183 | +## ActivityPub Federation |
| 184 | + |
| 185 | +The app implements the W3C ActivityPub protocol (Server-to-Server). All endpoints return `application/activity+json` content type for ActivityPub clients, and fall back to HTML for browsers. |
| 186 | + |
| 187 | +### Actor |
| 188 | + |
| 189 | +| Method | Route | Description | |
| 190 | +|--------|-------|-------------| |
| 191 | +| GET | `/users/{username}` | Get ActivityPub Actor object for a local user | |
| 192 | +| GET | `/@{username}/` | Alias for actor endpoint | |
| 193 | + |
| 194 | +### Inbox |
| 195 | + |
| 196 | +| Method | Route | Description | |
| 197 | +|--------|-------|-------------| |
| 198 | +| POST | `/@{username}/inbox` | Receive ActivityPub activities for a specific user | |
| 199 | +| GET | `/@{username}/inbox` | Get an empty OrderedCollection (inbox is not enumerable) | |
| 200 | +| POST | `/inbox` | Shared inbox — receives activities for all local actors | |
| 201 | + |
| 202 | +### Outbox |
| 203 | + |
| 204 | +| Method | Route | Description | |
| 205 | +|--------|-------|-------------| |
| 206 | +| GET | `/@{username}/outbox` | Get the user's outbox as an OrderedCollection | |
| 207 | +| POST | `/@{username}/outbox` | Create a new activity in the outbox | |
| 208 | + |
| 209 | +### Followers / Following |
| 210 | + |
| 211 | +| Method | Route | Description | |
| 212 | +|--------|-------|-------------| |
| 213 | +| GET | `/@{username}/followers` | Get the followers collection | |
| 214 | +| GET | `/@{username}/following` | Get the following collection | |
| 215 | + |
| 216 | +### Posts |
| 217 | + |
| 218 | +| Method | Route | Description | |
| 219 | +|--------|-------|-------------| |
| 220 | +| GET | `/@{username}/{token}` | Get a single post as ActivityPub JSON-LD | |
| 221 | + |
| 222 | +### Discovery |
| 223 | + |
| 224 | +| Route | Description | |
| 225 | +|-------|-------------| |
| 226 | +| `/.well-known/webfinger` | WebFinger endpoint for `acct:user@domain` lookups (returns `self` link to Actor) | |
| 227 | +| `/.well-known/nodeinfo/2.0` | NodeInfo 2.0 document | |
| 228 | + |
| 229 | +--- |
| 230 | + |
| 231 | +## Legacy OStatus |
| 232 | + |
| 233 | +| Method | Route | Description | |
| 234 | +|--------|-------|-------------| |
| 235 | +| GET | `/ostatus/follow/` | Renders follow page via OStatus (`?uri=`) | |
| 236 | +| GET | `/api/v1/ostatus/followRemote/{local}` | Render guest follow page for remote account | |
| 237 | +| GET | `/api/v1/ostatus/link/{local}/{account}` | Perform WebFinger and get OStatus subscribe link | |
| 238 | + |
| 239 | +--- |
| 240 | + |
| 241 | +## Frontend / Document Serving |
| 242 | + |
| 243 | +| Method | Route | Description | |
| 244 | +|--------|-------|-------------| |
| 245 | +| GET | `/` | Main SPA entry point (renders the Vue app) | |
| 246 | +| GET | `/timeline/{path}` | SPA timeline route | |
| 247 | +| GET | `/document/get` | Get a cached document by ID (requires auth) | |
| 248 | +| GET | `/document/public` | Get a public cached document by ID (no auth) | |
| 249 | +| GET | `/document/get/resized` | Get a resized/preview of a cached document (requires auth) | |
| 250 | +| GET | `/document/public/resized` | Get a public resized/preview document (no auth) | |
| 251 | + |
| 252 | +--- |
| 253 | + |
| 254 | +## Async Queue |
| 255 | + |
| 256 | +| Method | Route | Description | |
| 257 | +|--------|-------|-------------| |
| 258 | +| POST | `/async/request/{token}` | Process queued ActivityPub federation requests for a given token | |
| 259 | + |
| 260 | +--- |
| 261 | + |
| 262 | +## Error Responses |
| 263 | + |
| 264 | +The API returns standard HTTP status codes: |
| 265 | + |
| 266 | +| Status | Meaning | |
| 267 | +|--------|---------| |
| 268 | +| 200 | Success | |
| 269 | +| 201 | Created | |
| 270 | +| 204 | No Content (deletion success) | |
| 271 | +| 400 | Bad Request | |
| 272 | +| 401 | Unauthorized | |
| 273 | +| 403 | Forbidden | |
| 274 | +| 404 | Not Found | |
| 275 | +| 405 | Method Not Allowed | |
| 276 | +| 500 | Internal Server Error | |
| 277 | + |
| 278 | +Error bodies are JSON: `{"message": "...", "code": ...}` |
0 commit comments