Install
openclaw skills install nextcloud-aio-ocReliable Nextcloud integration for Notes, Tasks, Calendar, Files, and Contacts (CardDAV-safe vCard handling with robust contact create/update).
openclaw skills install nextcloud-aio-ocThis skill connects OpenClaw to Nextcloud for:
Primary goal: Predictable, loss-resistant Nextcloud reads/writes.
This skill is validated against:
32.0.6)2026.3.2)Set these environment variables:
NEXTCLOUD_URL (example: https://cloud.example.com)NEXTCLOUD_USERNEXTCLOUD_TOKEN (App Password strongly preferred)NEXTCLOUD_TIMEZONE (recommended, IANA timezone like America/Los_Angeles; used for timezone-less date/time inputs)This section is the source of truth for registry metadata parity.
node (Node.js 20+)NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_TOKENNEXTCLOUD_TIMEZONENEXTCLOUD_URL (plus normal redirects from that host)If a registry entry says "no required binaries" or "no required env vars", treat it as stale metadata and fix before publishing.
Before first execution in any new environment:
SKILL.md, scripts, examples, commit messages, or changelog text..env files out of git (see .gitignore).The skill ships two scripts: scripts/nextcloud.js (Node.js, text operations) and scripts/files_binary.py (Python 3, binary file transfers). Review both before running in new environments:
node --version (must be 20+)rg -n "https?://" scripts/nextcloud.jsrg -n "child_process|spawn\\(|exec\\(|require\\(\"fs\"\\)|require\\('fs'\\)|fs\\." scripts/nextcloud.jsrg -n "NEXTCLOUD_URL|NEXTCLOUD_USER|NEXTCLOUD_TOKEN|NEXTCLOUD_TIMEZONE" scripts/nextcloud.jsIf review confidence is low, do not run until code is reviewed by a trusted maintainer.
Quick grep-style checks on scripts/nextcloud.js should currently show:
process.env.NEXTCLOUD_URL|USER|TOKEN|TIMEZONE.${CONFIG.url}${endpoint} and uses fetch(...).child_process or fs usage in the bundle entry path.Quick checks on scripts/files_binary.py:
os.environ.get("NEXTCLOUD_URL|USER|TOKEN").{url}/remote.php/dav/files/{user}/{path}.urllib, base64, xml.etree); no third-party deps.This is not a full security audit. Re-run checks after every bundle update.
node scripts/nextcloud.js <command> <subcommand> [options]
notes listnotes get --id <id>notes create --title <title> --content <content> [--category <category>]notes edit --id <id> [--title <title>] [--content <content>] [--category <category>]notes delete --id <id>tasks list [--calendar <calendar-name>]tasks create --title <title> [--calendar <calendar-name>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]tasks edit --uid <uid> [--calendar <calendar-name>] [--title <title>] [--due <iso>] [--priority <0-9>] [--description <text>] [--timezone <IANA>]tasks delete --uid <uid> [--calendar <calendar-name>]tasks complete --uid <uid> [--calendar <calendar-name>]calendar list [--from <iso>] [--to <iso>] (default: next 7 days)calendar create --summary <summary> --start <iso-or-date> --end <iso-or-date> [--calendar <calendar-name>] [--description <text>] [--timezone <IANA>] [--all-day]calendar edit --uid <uid> [--calendar <calendar-name>] [--summary <summary>] [--start <iso-or-date>] [--end <iso-or-date>] [--description <text>] [--timezone <IANA>] [--all-day]calendar delete --uid <uid> [--calendar <calendar-name>]Important:
Contact birthdays appears as an events calendar but is read-only.contacts create/edit --birthday ..., not through calendar create/edit.calendar list returns event summary, start, end, and may include location and description when present.calendars list [--type <tasks|events>]files list [--path <path>]files search --query <query> (supports natural-language input; ranks by filename/path token relevance)files get --path <path> (accepts relative user path or full DAV href)files upload --path <path> --content <content>files delete --path <path>nextcloud.js passes file content as text strings, which corrupts binary formats.
Use the companion Python script for any binary file:
python3 scripts/files_binary.py download <nc_path> <local_path>
python3 scripts/files_binary.py upload <local_path> <nc_path>
python3 scripts/files_binary.py exists <nc_path>
python3 scripts/files_binary.py list [<nc_path>]
Reads the same NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_TOKEN env vars.
Auto-detects MIME type from file extension (ODT, DOCX, XLSX, PDF, PNG, JPG, and more).
When to use which:
| Situation | Command |
|---|---|
Plain text files (.md, .txt, .csv) | node scripts/nextcloud.js files get/upload |
Binary files (.odt, .docx, .pdf, images) | python3 scripts/files_binary.py download/upload |
contacts list [--addressbook <name>]contacts get --uid <uid> [--addressbook <name>]contacts search --query <query> [--addressbook <name>]contacts create [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--addressbook <name>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]contacts edit --uid <uid> [--addressbook <name>] [--name <full-name>] [--first-name <given>] [--last-name <family>] [--middle-name <middle>] [--prefix <prefix>] [--suffix <suffix>] [--email <single>] [--emails <csv>] [--phone <single>] [--phones <csv>] [--organization <org>] [--title <title>] [--note <note>] [--birthday <YYYY-MM-DD|YYYYMMDD|--MM-DD|--MMDD>]contacts delete --uid <uid> [--addressbook <name>]addressbooks listUse fullName and structuredName as canonical name fields.
Contact output includes:
uidaddressBookfullNamestructuredName with:
familyNamegivenNameadditionalNameshonorificPrefixeshonorificSuffixesnameRaw (raw N value for diagnostics only; do not display to users by default)emails array or nullphones array or nullorganization, title, notebirthday normalized for readability (YYYY-MM-DD or --MM-DD when possible)birthdayRaw (raw CardDAV value)Apply these on every write to prevent corruption and confusion:
UID from URL; always use payload uid.fullName and structuredName for user-facing names; keep nameRaw diagnostic-only.YYYY-MM-DD, YYYYMMDD, --MM-DD, --MMDD.BDAY.contacts create/edit --birthday ...; never edit birthday-derived calendar entries directly.--addressbook; otherwise use remembered default or ask once.end > start for timed events.SUMMARY, DESCRIPTION).0..9).--timezone or NEXTCLOUD_TIMEZONE.VALUE=DATE semantics with exclusive DTEND.Contact birthdays as read-only/system calendar.VEVENT + VTODO); keep them eligible for both event/task flows.files upload/get/delete.create, edit, complete, delete), verify with follow-up read/list when practical.0.5s, 1s, 2s before declaring failure.3.0 vs 4.0) and content on write/read.FN and N should each appear once in valid contacts.BDAY failures are often malformed input (for example stray delimiters), not server instability.HTTP 403 on calendar create/edit/delete -> likely read-only/system calendar or missing permission; switch to writable events calendar.HTTP 501 on files search -> WebDAV SEARCH unsupported; fallback to recursive PROPFIND + client-side filter.HTTP 415 on task/event update -> malformed ICS payload; normalize formatting and retry once.When user creates tasks/events without explicit calendar:
calendars list --type tasks or calendars list --type events.events, filter out likely read-only calendars first (Contact birthdays, Holidays, names containing read only or readonly).Memory keys:
default_task_calendardefault_event_calendarWhen user creates contacts without explicit address book:
addressbooks list.Memory key:
default_addressbookThese playbooks are execution shortcuts. Reliability and safety rules above still apply.
--addressbook or remembered default).--name for full-name input; use structured-name flags for split-name input.contacts create/edit --birthday ...), never calendar mutation commands.contacts get --uid ... when confidence is important.0..9) before write.Contact birthdays and system calendars as read-only.start/end and require end > start for timed events.--timezone or use NEXTCLOUD_TIMEZONE.--all-day with date values (not timed midnight ranges).All-day end-date normalization behavior:
start=YYYY-MM-DD, end=YYYY-MM-DD -> inclusive single-day input, normalized.start=YYYY-MM-DD, end=next-day -> already-exclusive single-day input.DTEND.Use this when user asks for appointment details, address, or "what is my appointment today":
NEXTCLOUD_TIMEZONE (or ask if timezone is unclear).calendar list --from <start-of-day-iso> --to <end-of-day-iso>.dermatology, doctor, provider name).summary, time window, location, and key lines from description.files get/upload/delete accept either a relative user path (for example /Share-Family/file.docx) or a full DAV href returned by search.python3 scripts/files_binary.py — never pass binary content through nextcloud.js files upload.files search with user phrasing directly; it auto-normalizes query text and ranks likely matches.When validating this skill in a live environment:
Use a unique timestamped suffix on all test data and clean up all temporary artifacts unless the user requests keeping them.
Use least-privilege credentials for tests and monitor outbound traffic; the process should only communicate with NEXTCLOUD_URL.
Calendar exception handling:
calendar list.Persist these high-value rules in memory/rules:
contacts create/edit --birthday ...; never mutate Contact birthdays directly.calendar create/edit --all-day with date values (avoid timed midnight ranges).--timezone or NEXTCLOUD_TIMEZONE; ask if timezone is unknown.403, report likely read-only/permission issue.VALUE=DATE); avoid duplicate compatibility events unless explicitly requested.Birthday was updated but not visible in Nextcloud UI yet:
contacts get --uid ... immediately.contacts get and contacts search disagree briefly:
Event creation keeps failing:
calendars list --type events and verify at least one writable non-system calendar exists.All commands return JSON:
Success:
{
"status": "success",
"data": {}
}
Error:
{
"status": "error",
"message": "Error description"
}