> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Nextcloud

> Client-mode REST API connector for self-hosted Nextcloud: create shift reports, batch records and setpoint tables from MQTT as files (WebDAV) without overwriting or as new versions, download recipes and configs, browse folders, search files, comment on files, share with public links, push alarms as Nextcloud notifications or Talk messages, set user statuses, read upcoming maintenance events, follow the activity stream and register webhook listeners

Technical Preview

## Overview

Client-mode connector for a self-hosted [Nextcloud](https://nextcloud.com) server that moves plant files and messages between the broker and Nextcloud. A typical flow writes a shift report, batch record or alarm snapshot as a JSON or CSV file into a plant folder (the folder is created on the fly), writes today's setpoint table over yesterday's while Nextcloud keeps the old one as a version, reads a recipe back when a line changes over, comments on a batch record when QA releases it, shares a weekly report with a public link that expires, and sends an alarm to the maintenance team as a Nextcloud notification or a message in a Talk conversation. New files can be picked up from the activity stream or with a webhook listener that calls the broker.

The template file `NextcloudRestApi.lot` holds one connector, `NextcloudRestApi`, with 43 events (29 GET, 9 POST, 5 PUT; 13 writes, no delete). Files go through WebDAV (`/remote.php/dav/files/...`), everything else through the OCS API (`/ocs/v2.php/...`) of the server and its apps: provisioning, sharing, notifications, activity, user status, calendar (dav), Talk (spreed) and webhook listeners. Topics use `storage/nextcloud/<object>/<verb>`; each response is published on `<source topic>/response`.

> [!NOTE]
>
> The connector was checked against the Nextcloud OpenAPI specs (server core, provisioning, files, sharing, dav, user status and webhook listeners; notifications and Talk) for method, path, path parameters, required headers and query parameters and every example body, against the Nextcloud developer manual for WebDAV, comments and activity, and against a local mock. It was then run live against a local Nextcloud 35.0.1 server (official Docker image) with Talk 25.0.5: all 43 events answered as expected, including a file created, read back, overwritten as a new version and shared by public link, a notification, a Talk conversation with a message and a webhook listener. Every authenticated event answered `401` to a wrong app password. The local test used plain HTTP on localhost and called the server directly, not through a broker.

## Setup

1. Point the connector at your server: replace `BASE_ADDRESS` (`https://nextcloud.example.com` in the template) with your Nextcloud address, including the web root if Nextcloud does not run at the domain root (for example `https://cloud.example.com` or `https://example.com/nextcloud`), without a trailing slash. Keep this value as `NEXTCLOUD_BASE_URL` in your deployment notes. For plain HTTP on a plant LAN use `http://...` and set `USE_SSL "false"`. The host name must be in the server's `trusted_domains`.
2. Create a dedicated account for the broker, for example `plant-bot`, and give it a storage quota. Share the plant folders with it, or let it own them.
3. Log in as that account and create an app password under **Personal settings > Security > Devices & sessions > Create new app password**. Use the app password, never the login password: accounts with two-factor authentication refuse login passwords, and you can revoke an app password on its own.
4. Admin-only events (`UsersList`, `GroupsList`, `GroupUsersList`, `AdminNotificationSend` and the webhook events) need an app password of an administrator. Group sub-admins can list the users and groups they manage. Webhook listeners can also be delegated to a group through the **Webhooks** admin privilege.
5. Talk events need the **Talk** app, webhook events the **Webhook listeners** app (Nextcloud 30 and later, shipped but check that it is enabled), and activity events the **Activity** app.
6. Keep `NEXTCLOUD_USER`, `NEXTCLOUD_APP_PASSWORD` and the derived `NEXTCLOUD_BASIC_AUTH` (see below) in broker secrets and inject them with `GET SECRET`.

## Authentication

Every event except `StatusGet` sends `Authorization: Basic <credentials>`, built from the `basic_auth` field of the payload. `basic_auth` is the Base64 of `<NEXTCLOUD_USER>:<NEXTCLOUD_APP_PASSWORD>`, for example `plant-bot:Ab12C-dE34f-...`. Store that value as the secret `NEXTCLOUD_BASIC_AUTH`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "BASE64_OF_USER_COLON_APP_PASSWORD"}
```

OCS events also send `OCS-APIRequest: true` (without it Nextcloud answers `412 CSRF check failed`) and ask for JSON with `?format=json`. OCS answers in an envelope: the result is in `ocs.data`, the status in `ocs.meta`.

As an alternative, the connector can carry the credentials itself with `WITH USERNAME` and `WITH PASSWORD` in `REST_API_CONFIG`. In that case, remove the `Authorization` header lines from the events.

> [!WARNING]
>
> `basic_auth` grants full access to the account's files. Never retain topics that carry it, restrict `storage/nextcloud/#` with ACLs, and give the broker its own account instead of a personal one. Nextcloud's brute-force protection slows down and then blocks (`429`) the broker's IP address after repeated wrong passwords, also for correct requests. Fix the secret first, then reset the block with `occ security:bruteforce:reset <ip>`.

## Payloads

| Field | Used by | Content |
| - | - | - |
| `basic_auth` | every event except `StatusGet` | Base64 of `user:app-password` |
| `user` | `FileUpload`, `FileVersionUpload`, `FileDownload` | Nextcloud user id in the WebDAV path, usually the broker account |
| `path` | `FileUpload`, `FileVersionUpload`, `FileDownload` | File path below the user's root, URL-encoded per segment (a space is `%20`), no leading slash, for example `Plant/Line2/2026-10-05-shift-A.json` |
| `content` | `FileUpload`, `FileVersionUpload` | File content as a string (JSON text, CSV, plain text) |
| `user_id`, `group_id`, `share_id`, `notification_id`, `file_id`, `provider_id`, `filter`, `room_token`, `webhook_id` | events with that id in the path | Ids as strings. `file_id` is the numeric file id, `room_token` the Talk conversation token (4 to 30 lowercase letters and digits). |
| `query` | events whose endpoint ends with `&{value.json.query}` | URL query string without `?`, for example `search=operator&limit=50`, or `""` |
| `body` | POST and PUT OCS events, `FileCommentCreate` | JSON request body |

Send `query` as an empty string when you need no parameters; the request then ends with `&`, which Nextcloud ignores.

## Uploading files

`FileUpload` writes a new file with WebDAV `PUT`. It sends `If-None-Match: *`, so an existing file is never overwritten: Nextcloud answers `412 Precondition Failed` instead. It also sends `X-NC-WebDAV-Auto-Mkcol: 1`, so Nextcloud 32 and later create missing parent folders. Older servers answer `404` or `409` when the folder does not exist yet; create it by hand once.

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "user": "plant-bot", "path": "Plant/Line2/2026-10-05-shift-A.json", "content": "{\"line\":\"2\",\"shift\":\"A\",\"oee\":0.87,\"scrap\":14,\"operator\":\"Jo\u00e3o\"}"}
```

Nextcloud answers `201 Created` with an empty body. The new file id is in the `OC-FileId` response header (`00000091oc...`: the number before the instance suffix is the file id). Use `Search` (provider `files`) or `ActivityList` to find file ids later.

`FileVersionUpload` writes a file whether it exists or not (`201` for a new file, `204` when it replaced one). With the Versions app, the previous content stays in the file's version history, so writing today's setpoint table over yesterday's keeps yesterday's as a version. `FileDownload` returns the raw file content. Keep files to text, CSV or JSON that fits an MQTT message.

Build the payload in a LoT action:

```lot theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION NextcloudShiftReportUpload
ON TOPIC "plant/line2/shift/report" DO
    SET "auth" WITH GET SECRET "NEXTCLOUD_BASIC_AUTH"
    SET "user" WITH GET SECRET "NEXTCLOUD_USER"
    SET "path" WITH GET JSON "path" IN PAYLOAD AS STRING
    SET "content" WITH GET JSON "content" IN PAYLOAD AS STRING
    PUBLISH TOPIC "storage/nextcloud/files/upload" WITH { "basic_auth": {auth}, "user": {user}, "path": {path}, "content": {content} }
```

## Examples

Health check without credentials (`storage/nextcloud/status/get`), for example from a timer:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{}
```

The answer carries `installed`, `maintenance`, `needsDbUpgrade` and `versionstring`.

Read a recipe back on changeover (`storage/nextcloud/files/download`):

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "user": "plant-bot", "path": "Plant/Line2/recipes/PX-200.json"}
```

List the sub-folders of the plant folder (`storage/nextcloud/folders/tree/get`):

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "query": "path=/Plant&depth=2"}
```

Find files by name (`storage/nextcloud/search/query`); each entry carries the path in `subline` and the file id in `attributes.fileId`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "provider_id": "files", "query": "term=shift-A&limit=20"}
```

Share a shift report with a public, read-only link that expires (`storage/nextcloud/shares/create`); the answer carries `url`, `token` and `id`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "body": {"path": "/Plant/Line2/2026-10-05-shift-A.json", "shareType": 3, "permissions": 1, "expireDate": "2026-10-31", "label": "Shift A report"}}
```

`shareType` is `0` for a user, `1` for a group, `3` for a public link and `4` for email; `permissions` adds up `1` read, `2` update, `4` create, `8` delete and `16` share. End a link early with `ShareUpdate` and an `expireDate` of today instead of deleting it.

Comment on a batch record when QA releases it (`storage/nextcloud/files/comments/create`):

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "file_id": "91", "body": {"actorType": "users", "verb": "comment", "message": "Batch 1005-A released by QA", "objectType": "files"}}
```

Send an alarm to a user's Nextcloud notifications, which also reach the desktop and mobile apps (`storage/nextcloud/notifications/admin/send`, admin only):

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "user_id": "operator1", "body": {"subject": "Line 2: spindle temperature high", "message": "TT-201 92 C (limit 85 C) at 14:05"}}
```

Post an alarm into a Talk conversation (`storage/nextcloud/talk/messages/send`). Create the conversation once with `TalkRoomCreate` (`{"roomType": 2, "roomName": "Line 2 alarms"}`), add people with `TalkParticipantAdd`, and keep its `token`:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "room_token": "abcd1234", "body": {"message": "Line 2: TT-201 92 C (limit 85 C)", "silent": false}}
```

```lot theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ACTION NextcloudAlarmToTalk
ON TOPIC "plant/line2/alarms" DO
    SET "auth" WITH GET SECRET "NEXTCLOUD_BASIC_AUTH"
    SET "text" WITH GET JSON "text" IN PAYLOAD AS STRING
    PUBLISH TOPIC "storage/nextcloud/talk/messages/send" WITH { "basic_auth": {auth}, "room_token": "abcd1234", "body": { "message": {text} } }
```

Show that a line account is busy (`storage/nextcloud/user-status/set`, then `storage/nextcloud/user-status/message/set`):

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "body": {"statusType": "dnd"}}
```

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "body": {"message": "Line 2 in maintenance until 16:00", "clearAt": null}}
```

Pick up new files without a public endpoint by polling the activity stream (`storage/nextcloud/activity/list`): ask for `filter` `files` with `query` `since=<last activity_id>&sort=asc&limit=50`, store the `X-Activity-Last-Given` id (or the last `activity_id`), and treat `304` as nothing new. Use `filter` `filter` with `object_type=files&object_id=<id>` for the history of one file.

Or let Nextcloud call the broker when a file is created in the plant folder (`storage/nextcloud/webhooks/create`, admin only). The `uri` is a broker REST\_API server connector that Nextcloud can reach; `authData` sets a header the receiver checks:

```json theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
{"basic_auth": "<NEXTCLOUD_BASIC_AUTH>", "body": {"httpMethod": "POST", "uri": "https://broker.example.com/nextcloud/webhooks", "event": "OCP\\Files\\Events\\Node\\NodeCreatedEvent", "eventFilter": {"event.node.path": "/^\\/plant-bot\\/files\\/Plant\\//"}, "authMethod": "header", "authData": {"X-Webhook-Token": "<secret>"}}}
```

Nextcloud sends webhooks from background jobs, so deliveries depend on its cron and can lag behind.

## Events

| Event | Method | Endpoint | Source topic | Write | Notes |
| - | - | - | - | - | - |
| `StatusGet` | `GET` | `/status.php` | `storage/nextcloud/status/get` | | Smoke and health check without credentials: installed, maintenance, needsDbUpgrade, version, productname. Send `{}` (no basic\_auth needed). |
| `CapabilitiesGet` | `GET` | `/ocs/v2.php/cloud/capabilities?format=json` | `storage/nextcloud/capabilities/get` | | Server version and what the instance supports (files versioning, sharing defaults and enforced expiry, notifications, user\_status, spreed/Talk features, webhook\_listeners). Works without login too; with basic\_auth it adds user-specific capabilities. |
| `UserMe` | `GET` | `/ocs/v2.php/cloud/user?format=json` | `storage/nextcloud/users/me/get` | | Smoke: the user behind the app password (id, display-name, email, groups, quota.used / quota.free / quota.relative, language). |
| `UsersList` | `GET` | `/ocs/v2.php/cloud/users?format=json&{value.json.query}` | `storage/nextcloud/users/list` | | User ids (admin or group sub-admin only). query e.g. search=operator\&limit=50\&offset=0 (or empty). |
| `UserGet` | `GET` | `/ocs/v2.php/cloud/users/{value.json.user_id}?format=json` | `storage/nextcloud/users/get` | | One user: display name, email, groups, enabled, last login and quota (watch the storage quota of a plant account). Admins see everyone, users only themselves. |
| `GroupsList` | `GET` | `/ocs/v2.php/cloud/groups?format=json&{value.json.query}` | `storage/nextcloud/groups/list` | | Group ids (admin or sub-admin). query e.g. search=line\&limit=50 (or empty). |
| `GroupUsersList` | `GET` | `/ocs/v2.php/cloud/groups/{value.json.group_id}/users?format=json` | `storage/nextcloud/groups/users/list` | | Members of a group, e.g. who is on the line-2 maintenance team (admin or sub-admin of that group). |
| `FileUpload` | `PUT` | `/remote.php/dav/files/{value.json.user}/{value.json.path}` | `storage/nextcloud/files/upload` | Yes | Catalog 'PUT file via WebDAV'. Create a new file (JSON / CSV / text shift report, batch record, alarm snapshot) from an MQTT payload. Sends If-None-Match: \* so an existing file is never overwritten (412 Precondition Failed instead) and X-NC-WebDAV-Auto-Mkcol: 1 so missing parent folders are created (Nextcloud 32+; older servers answer 404/409 if the folder is missing). path is relative to the user's root, URL-encoded per segment, no leading slash. 201 Created, file id in the OC-FileId response header, empty body. |
| `FileVersionUpload` | `PUT` | `/remote.php/dav/files/{value.json.user}/{value.json.path}` | `storage/nextcloud/files/versions/upload` | Yes | Write a file whether or not it exists (e.g. today's setpoint table over yesterday's). With the Versions app the previous content stays in the file's version history. Also creates missing parent folders (X-NC-WebDAV-Auto-Mkcol: 1). 201 if new, 204 if replaced. |
| `FileDownload` | `GET` | `/remote.php/dav/files/{value.json.user}/{value.json.path}` | `storage/nextcloud/files/download` | | Read a file back (recipe, config, setpoint table) on changeover. Returns the raw content; keep to text / JSON that fits an MQTT message. 404 if missing. |
| `FolderTreeGet` | `GET` | `/ocs/v2.php/apps/files/api/v1/folder-tree?format=json&{value.json.query}` | `storage/nextcloud/folders/tree/get` | | Sub-folders of a folder (id, basename, children) without WebDAV PROPFIND. query e.g. path=/Plant\&depth=2 (folders only; list files with Search provider files or with Activity). |
| `FileDirectLinkCreate` | `POST` | `/ocs/v2.php/apps/dav/api/v1/direct?format=json` | `storage/nextcloud/files/direct-link/create` | | Temporary direct download URL for a file id (no login needed to fetch it), e.g. to hand a report to an HMI or a printer. body: `{"fileId":91,"expirationTime":3600}` (seconds; server default 8 hours, max 24 hours). Creates no content (read-only POST). Treat the returned URL as a secret. |
| `FileCommentCreate` | `POST` | `/remote.php/dav/comments/files/{value.json.file_id}` | `storage/nextcloud/files/comments/create` | Yes | Comment on a file (e.g. 'Batch 1005-A released by QA', shown in the file's Activity / Comments sidebar). file\_id is the numeric file id (OC-FileId without the instance suffix, or fileId from Search / Activity). body: `{"actorType":"users","verb":"comment","message":"...","objectType":"files"}`. 201, comment URL in Content-Location. |
| `SearchProvidersList` | `GET` | `/ocs/v2.php/search/providers?format=json` | `storage/nextcloud/search/providers/list` | | Unified search providers on this instance (files, talk-message, comments, calendar, ...). |
| `Search` | `GET` | `/ocs/v2.php/search/providers/{value.json.provider_id}/search?format=json&{value.json.query}` | `storage/nextcloud/search/query` | | Unified search in one provider, e.g. provider\_id files with query term=shift-A\&limit=20 (entries carry title, subline (path), resourceUrl and attributes.fileId); talk-message or comments work the same. Page with cursor=`<cursor>`. |
| `SharesList` | `GET` | `/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json&{value.json.query}` | `storage/nextcloud/shares/list` | | Shares created by the user. query e.g. path=/Plant/Line2/2026-10-05-shift-A.json\&reshares=true, or subfiles=true\&path=/Plant (or empty). |
| `ShareGet` | `GET` | `/ocs/v2.php/apps/files_sharing/api/v1/shares/{value.json.share_id}?format=json` | `storage/nextcloud/shares/get` | | One share (url, token, permissions, expiration, share\_with). |
| `ShareCreate` | `POST` | `/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json` | `storage/nextcloud/shares/create` | Yes | Catalog 'POST OCS share link'. Share a file or folder. Public link: `{"path":"/Plant/Line2/2026-10-05-shift-A.json","shareType":3,"permissions":1,"expireDate":"2026-10-31","label":"Shift A report"}` (shareType 3 = public link, 0 = user, 1 = group, 4 = email; permissions 1 read, 2 update, 4 create, 8 delete, 16 share, 31 all; add password for protected links). The response carries url and token. |
| `ShareUpdate` | `PUT` | `/ocs/v2.php/apps/files_sharing/api/v1/shares/{value.json.share_id}?format=json` | `storage/nextcloud/shares/update` | Yes | Change a share: permissions, password, expireDate (end a link early instead of deleting it), note, label. body e.g. `{"expireDate":"2026-10-07","note":"Superseded by shift B"}`. |
| `SharesPendingList` | `GET` | `/ocs/v2.php/apps/files_sharing/api/v1/shares/pending?format=json` | `storage/nextcloud/shares/pending/list` | | Shares offered to the user that are not accepted yet. |
| `ShareesSearch` | `GET` | `/ocs/v2.php/apps/files_sharing/api/v1/sharees?format=json&{value.json.query}` | `storage/nextcloud/sharees/search` | | Find users, groups or emails to share with. query e.g. search=operator\&itemType=file\&perPage=20. |
| `NotificationsList` | `GET` | `/ocs/v2.php/apps/notifications/api/v2/notifications?format=json` | `storage/nextcloud/notifications/list` | | The user's notifications (share offers, mentions, admin notifications, app updates). 204 when there are none. |
| `NotificationGet` | `GET` | `/ocs/v2.php/apps/notifications/api/v2/notifications/{value.json.notification_id}?format=json` | `storage/nextcloud/notifications/get` | | One notification by id. |
| `AdminNotificationSend` | `POST` | `/ocs/v2.php/apps/notifications/api/v3/admin_notifications/{value.json.user_id}?format=json` | `storage/nextcloud/notifications/admin/send` | Yes | Push an alarm or shift note to a user's Nextcloud notifications (web, desktop and mobile apps). Admin app password only. body: `{"subject":"Line 2: spindle temperature high","message":"TT-201 92 C (limit 85 C) at 14:05"}` (subject up to 255, message up to 4000 characters). v3 API; older servers use v2 with shortMessage / longMessage. |
| `ActivityList` | `GET` | `/ocs/v2.php/apps/activity/api/v2/activity/{value.json.filter}?format=json&{value.json.query}` | `storage/nextcloud/activity/list` | | Activity stream (file created / changed / shared / commented) to pick up new files without a public endpoint. filter all, self, by, files, files\_sharing, files\_favorites or filter (one object: query object\_type=files\&object\_id=91). query e.g. since=`<last activity_id>`\&limit=50\&sort=asc. 304 when nothing is new; the X-Activity-Last-Given header holds the id to continue from. |
| `ActivityFiltersList` | `GET` | `/ocs/v2.php/apps/activity/api/v2/activity/filters?format=json` | `storage/nextcloud/activity/filters/list` | | Available activity filters (id, name, priority). |
| `UserStatusGet` | `GET` | `/ocs/v2.php/apps/user_status/api/v1/user_status?format=json` | `storage/nextcloud/user-status/get` | | Own status (status online / away / dnd / invisible / offline, message, icon, clearAt). |
| `UserStatusSet` | `PUT` | `/ocs/v2.php/apps/user_status/api/v1/user_status/status?format=json` | `storage/nextcloud/user-status/set` | Yes | Set the account's status, e.g. a line or kiosk account goes dnd while the machine runs a batch. body: `{"statusType":"dnd"}` (online, away, dnd, invisible, offline). |
| `UserStatusMessageSet` | `PUT` | `/ocs/v2.php/apps/user_status/api/v1/user_status/message/custom?format=json` | `storage/nextcloud/user-status/message/set` | Yes | Set a custom status message, e.g. `{"statusIcon":"<one emoji>","message":"Line 2 in maintenance until 16:00","clearAt":1791298800}` (clearAt Unix seconds, or null). |
| `UserStatusesList` | `GET` | `/ocs/v2.php/apps/user_status/api/v1/statuses?format=json&{value.json.query}` | `storage/nextcloud/user-status/list` | | Statuses of all users who set one (who is online on shift). query e.g. limit=100\&offset=0 (or empty). |
| `UserStatusOfUserGet` | `GET` | `/ocs/v2.php/apps/user_status/api/v1/statuses/{value.json.user_id}?format=json` | `storage/nextcloud/user-status/user/get` | | Status of one user (404 if the user never set one). |
| `UpcomingEventsList` | `GET` | `/ocs/v2.php/apps/dav/api/v1/events/upcoming?format=json&{value.json.query}` | `storage/nextcloud/calendar/upcoming/list` | | Upcoming events of the user's calendars (planned maintenance, shift changes, audits). query e.g. location=Line%202 (or empty). |
| `TalkRoomsList` | `GET` | `/ocs/v2.php/apps/spreed/api/v4/room?format=json&{value.json.query}` | `storage/nextcloud/talk/rooms/list` | | Talk conversations of the user (token, name, type, unreadMessages, lastActivity). query e.g. noStatusUpdate=1\&includeLastMessage=false (or empty). Needs the Talk app. |
| `TalkRoomGet` | `GET` | `/ocs/v2.php/apps/spreed/api/v4/room/{value.json.room_token}?format=json` | `storage/nextcloud/talk/rooms/get` | | One conversation by token. |
| `TalkRoomCreate` | `POST` | `/ocs/v2.php/apps/spreed/api/v4/room?format=json` | `storage/nextcloud/talk/rooms/create` | Yes | Create a conversation, e.g. one per line for alarms. body: `{"roomType":2,"roomName":"Line 2 alarms"}` (2 = group, 3 = public; 1 = one-to-one with invite). Returns the room token. |
| `TalkParticipantAdd` | `POST` | `/ocs/v2.php/apps/spreed/api/v4/room/{value.json.room_token}/participants?format=json` | `storage/nextcloud/talk/participants/add` | Yes | Add a user, group or team to a conversation (moderator rights needed). body: `{"newParticipant":"operator1","source":"users"}` (source users, groups, circles, emails, ...). |
| `TalkParticipantsList` | `GET` | `/ocs/v2.php/apps/spreed/api/v4/room/{value.json.room_token}/participants?format=json` | `storage/nextcloud/talk/participants/list` | | Participants of a conversation (actorId, displayName, participantType, inCall). |
| `TalkMessageSend` | `POST` | `/ocs/v2.php/apps/spreed/api/v1/chat/{value.json.room_token}?format=json` | `storage/nextcloud/talk/messages/send` | Yes | Catalog 'POST Talk message'. Post an alarm or shift note into a conversation. body: `{"message":"Line 2: TT-201 92 C (limit 85 C)","referenceId":"<sha256 hex of your alarm id, 64 chars>","silent":false}` (message up to 32000 characters; Markdown; referenceId lets you find the message again). 201 with the message. |
| `TalkMessagesList` | `GET` | `/ocs/v2.php/apps/spreed/api/v1/chat/{value.json.room_token}?format=json&lookIntoFuture=0&{value.json.query}` | `storage/nextcloud/talk/messages/list` | | Recent messages of a conversation (e.g. acknowledgements typed by operators). lookIntoFuture=0 is fixed (history, no long poll). query e.g. limit=20\&setReadMarker=0\&markNotificationsAsRead=0 (or empty); page back with lastKnownMessageId=`<id>`. |
| `WebhooksList` | `GET` | `/ocs/v2.php/apps/webhook_listeners/api/v1/webhooks?format=json` | `storage/nextcloud/webhooks/list` | | Registered webhook listeners (admin; Nextcloud 30+ with webhook\_listeners enabled). |
| `WebhookGet` | `GET` | `/ocs/v2.php/apps/webhook_listeners/api/v1/webhooks/{value.json.webhook_id}?format=json` | `storage/nextcloud/webhooks/get` | | One webhook listener by id. |
| `WebhookCreate` | `POST` | `/ocs/v2.php/apps/webhook_listeners/api/v1/webhooks?format=json` | `storage/nextcloud/webhooks/create` | Yes | Have Nextcloud call a broker REST\_API server connector when files change. Admin only. body: `{"httpMethod":"POST","uri":"https://broker.example.com/nextcloud/webhooks","event":"OCP\Files\Events\Node\NodeCreatedEvent","eventFilter":{"event.node.path":"/^\/plant-bot\/files\/Plant\//"},"authMethod":"header","authData":{"X-Webhook-Token":"<secret>"}}`. Deliveries run from background jobs (cron). |
| `WebhookUpdate` | `POST` | `/ocs/v2.php/apps/webhook_listeners/api/v1/webhooks/{value.json.webhook_id}?format=json` | `storage/nextcloud/webhooks/update` | Yes | Replace a webhook listener (same body as WebhookCreate; httpMethod, uri and event are required), e.g. listen to NodeWrittenEvent too or point it at a new broker. |

## Limits

* WebDAV `PUT` has no size limit of its own, but the web server and PHP limits apply (`upload_max_filesize`, `post_max_size`, proxy body size). Keep MQTT payloads small. Larger files need chunked uploads, which are not packaged.
* `X-NC-WebDAV-Auto-Mkcol` needs Nextcloud 32 or later. Admin notifications use the v3 API: `subject` up to 255 characters, `message` up to 4000. Talk messages are limited to 32000 characters.
* Nextcloud's brute-force protection delays and then blocks (`429`) an IP address after repeated failed logins. In the local test it blocked the client after 11 wrong passwords.
* Shares can have an enforced maximum expiry and password policy (see `CapabilitiesGet`, `files_sharing`). A share that breaks the policy answers `400` or `403`.
* `FolderTreeGet` lists folders only, and `Search` only matches names. File listings with sizes and dates need WebDAV `PROPFIND`, which is not a REST\_API method.

## Not packaged

* Delete events on purpose: files, folders, shares, notifications, Talk conversations and participants, status messages and webhook listeners.
* WebDAV methods that REST\_API does not offer (it supports `GET`, `POST`, `PUT`, `DELETE` and `PATCH`): `PROPFIND` (folder listing and file properties), `MKCOL` (create folder; `FileUpload` creates parents instead), `MOVE` and `COPY` (rename, archive, copy), `PROPPATCH` (favorites, tags), `REPORT` and `SEARCH` (filtered listings, comment lists), and the `MOVE`-based trash and version restore.
* Chunked uploads (v2) and bulk upload, public share (`/public.php`) uploads, folder downloads as ZIP or TAR, previews and thumbnails.
* User and group administration (create, edit, disable, wipe), app passwords and login flow, app config, Talk calls, polls, bots and breakout rooms, calendar and contacts writes (CalDAV and CardDAV), Deck, Forms and Tables.
* The webhook receiver. A receiver would be a separate `ENABLE_SERVER` REST\_API connector that checks the `authData` header.

## Suggested branches

`feat/rest-api-route-nextcloud` for the broker template and `feat/docs-rest-api-nextcloud` for this page.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.