MetaVox API Reference¶
Overview¶
The MetaVox API provides endpoints for metadata management, field configuration, views, column configuration, and batch operations.
Route Types¶
MetaVox exposes two types of API routes:
| Type | Base URL | Auth | Use case |
|---|---|---|---|
| OCS | /ocs/v2.php/apps/metavox/api/v1/ |
App password, OAuth, or session | External integrations, scripts, migrations |
| Browser | /apps/metavox/api/ |
Session (CSRF token required) | Internal UI, admin operations |
For external integrations, always use OCS routes — they don't require a CSRF token and work with app passwords.
Browser routes are used by the MetaVox frontend. They require a Nextcloud session and CSRF token. Some features (permissions, AI, backup, settings, telemetry) are only available via browser routes.
Architecture¶
| Controller | Responsibility |
|---|---|
ApiFieldController |
All OCS endpoints: fields, column config, batch operations, filter values, directory metadata |
ApiFilterController |
OCS endpoints: directory metadata, filter values, sorted file IDs |
ApiViewController |
OCS endpoints: view CRUD per groupfolder |
ViewController |
Browser-based view management (admin UI, requires session) |
LockController |
Cell lock/unlock for concurrent editing |
PresenceController |
Presence tracking (leave on tab close) |
AiAutofillController |
AI metadata generation |
BackupController |
Backup & restore operations |
SettingsController |
Admin settings (AI toggle) |
TelemetryController |
Usage telemetry reporting |
PermissionController |
Granular permission management |
FieldService |
Field definitions, metadata reads/writes — with distributed cache |
ViewService |
View CRUD — with distributed cache |
Important Notes¶
- All batch operations work with groupfolder file fields (stored in
metavox_gf_fieldsandmetavox_file_gf_meta) - Batch update/delete operations require both
file_idandgroupfolder_idper item - Read-only endpoints for stable data (column config, views, filter values) return
Cache-Control: privateheaders
Field Naming Convention¶
Field names (field_name) must follow a strict naming convention:
Rules¶
- Only lowercase letters (a-z), numbers (0-9), and underscores (_) are allowed
- Must start with a letter (not a number or underscore)
- No spaces, uppercase letters, or special characters
- Validated on both frontend and backend — invalid names are rejected with a
400 Bad Request
Prefix Convention¶
MetaVox uses prefixes to distinguish between field scopes:
| Scope | Prefix | Example | Description |
|---|---|---|---|
| Team folder metadata | gf_ |
gf_publication_status |
Metadata that applies to the team folder itself |
| File metadata | file_gf_ |
file_gf_department |
Metadata that applies to individual files within a team folder |
Important: The MetaVox admin UI adds the prefix automatically. The API does not — API consumers must include the correct prefix in the field_name when creating fields. If you omit the prefix, the field will be stored without it and may not appear correctly in the UI.
Examples¶
# Correct — file field with prefix
curl -X POST ".../api/v1/groupfolder-fields" \
-d '{"field_name": "file_gf_department", "field_label": "Department", "field_type": "select"}'
# Correct — team folder field with prefix
curl -X POST ".../api/v1/groupfolder-fields" \
-d '{"field_name": "gf_classification", "field_label": "Classification", "field_type": "text", "applies_to_groupfolder": 1}'
# Invalid — will be rejected (uppercase)
curl -X POST ".../api/v1/groupfolder-fields" \
-d '{"field_name": "file_gf_Department", ...}'
# → 400: "Field name may only contain lowercase letters, numbers and underscores"
# Invalid — will be rejected (spaces)
curl -X POST ".../api/v1/groupfolder-fields" \
-d '{"field_name": "file_gf_my field", ...}'
# → 400: "Field name may only contain lowercase letters, numbers and underscores"
Fields¶
Create a field¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolder-fields
Requires: Nextcloud admin
Request body — select / multiselect (list-shape field_options):
{
"field_name": "publicatiestatus",
"field_label": "Publicatiestatus",
"field_type": "select",
"field_description": "Status van de publicatie",
"field_options": ["Open", "Gesloten", "In behandeling"],
"is_required": false,
"sort_order": 1
}
Request body — date (object-shape field_options, since 2.1.0):
{
"field_name": "meeting_start",
"field_label": "Meeting start",
"field_type": "date",
"field_options": { "includeTime": true },
"is_required": false,
"applies_to_groupfolder": 1
}
For field_type: "date", field_options is an object with a single boolean includeTime:
- false (or omitted) → field stores YYYY-MM-DD
- true → field stores YYYY-MM-DDTHH:mm:ss (floating ISO 8601, no Z, no timezone)
See the Date / DateTime fields section below for the value-format contract on metadata writes, and the SharePoint migration mapping.
Validation errors (HTTP 400) — returned when field_options for a date field has the wrong shape:
{
"ocs": {
"meta": {"status": "failure", "statuscode": 400, "message": ""},
"data": {
"error": "Validation failed",
"fields": {
"field_options": "For date fields, field_options must be an object like {\"includeTime\": true}, not a list."
}
}
}
}
Possible field_options validation messages for date fields:
- For date fields, field_options must be an object like {"includeTime": true}. — non-array sent
- For date fields, field_options must be an object like {"includeTime": true}, not a list. — list-shape ["x"] sent
- Unknown keys for date field_options: <key>. Only "includeTime" is supported. — extra keys in the object
- "includeTime" must be a boolean (true/false). — non-bool-castable value
Truthy strings ("1", "true") and ints (1) are accepted and cast to true; falsy variants to false.
Update a field¶
Endpoint: PUT /ocs/v2.php/apps/metavox/api/v1/groupfolder-fields/{id}
Requires: Nextcloud admin
Body: same as create. The same field_options validation applies for date fields.
Flipping includeTime on an existing field does not rewrite previously-stored values — they remain in their original format. Existing date-only values rendered through a now-includeTime=true field are parsed as local midnight; existing datetime values rendered through a now-includeTime=false field display only the date portion.
Delete a field¶
Endpoint: DELETE /ocs/v2.php/apps/metavox/api/v1/groupfolder-fields/{id}
Requires: Nextcloud admin
Get all field definitions¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolder-fields
Requires: Nextcloud admin
Returns all defined fields across all groupfolders.
Assign fields to a groupfolder¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/fields
Requires: Nextcloud admin
Request body:
Get assigned fields for a groupfolder¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/fields
Returns the list of fields assigned to this groupfolder.
Get file-level fields for a groupfolder¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/file-fields
Returns fields assigned to this groupfolder that apply to individual files (not folder-level). Used by the view editor to populate column options.
File Metadata¶
Team-folder resolution (since 2.2.3). The group-folder-less endpoints (
.../files/{fileId}/metadataand the bulk read) resolve the team folder a file physically lives in, from your own mount — the same folder the Files UI shows. Reads and writes therefore always agree on one folder. A file that is not inside any team folder has no valid metadata scope and returns404 File is not in a team folderinstead of silently reading/writing nothing. The group-folder-scoped endpoints additionally reject a mismatch between the URL's{groupfolderId}and the file's actual folder with400 File is not in the specified team folder.
Get metadata for a single file¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/files/{fileId}/metadata
Returns all metadata for a file, scoped to the team folder it lives in. Requires
read access to the file. Returns 404 if the file is not in a team folder.
Response:
Save metadata for a single file¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/files/{fileId}/metadata
Accepts a partial metadata object — only the fields included are updated.
Requires write access. Writes into the team folder the file lives in; returns
404 if the file is not in a team folder.
Request body:
{
"metadata": {
"file_gf_publicatiestatus": "Gesloten",
"file_gf_verantwoordelijke": "a.jansen",
"file_gf_meeting_start": "2026-05-13T14:30:00",
"file_gf_publication_date": "2026-04-15"
}
}
Date / datetime values (since 2.1.0) — see Date / DateTime fields. Per-field format depends on the field's field_options.includeTime flag and is validated server-side. Empty string and null are always allowed (clear the value).
Validation errors (HTTP 400) — all-or-nothing: a single invalid date in the batch rejects the whole request, no partial save:
{
"ocs": {
"meta": {"status": "failure", "statuscode": 400, "message": ""},
"data": {
"error": "Validation failed",
"fields": {
"file_gf_meeting_start": "Expected YYYY-MM-DDTHH:mm:ss (floating ISO 8601, no timezone).",
"file_gf_publication_date": "Expected YYYY-MM-DD."
}
}
}
}
Get metadata for multiple files (bulk read)¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/files/metadata/bulk?file_ids=123,456,789
Returns metadata for up to 100 file IDs per request. Each file is scoped to its own team folder (resolved independently), so file 123 returns folder A's values and file 456 returns folder B's — no cross-folder mixing. Files you cannot access, or that are not in any team folder, are omitted from the response.
Response:
{
"ocs": {
"data": {
"123": {"file_gf_publicatiestatus": "Open"},
"456": {"file_gf_publicatiestatus": "Gesloten"}
}
}
}
Get file metadata within a groupfolder¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/files/{fileId}/metadata
Same as single file metadata, but the {groupfolderId} must match the folder the
file is actually in — otherwise 400 File is not in the specified team folder
(since 2.2.3). Use this form when you already know the folder; otherwise the
group-folder-less endpoint resolves it for you.
Save file metadata within a groupfolder¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/files/{fileId}/metadata
Same as single file save, but the {groupfolderId} must match the file's actual
folder — writing to a non-existent or mismatched folder returns 400 File is not
in the specified team folder (since 2.2.3), instead of silently storing the
value under a folder nothing reads back. Supports unlock and unlock_field
parameters to atomically save and release a cell lock. The same date-value
validation applies — see Date / DateTime fields.
Groupfolders¶
List all groupfolders¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders
Returns all groupfolders the authenticated user has access to.
Get groupfolder metadata¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/metadata
Returns folder-level metadata (not per-file).
Save groupfolder metadata¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/metadata
Same payload shape and date-value validation as the file-metadata save endpoints — see Date / DateTime fields.
Date / DateTime fields¶
Added in 2.1.0 — resolves SharePoint migration data loss (#65).
A single field_type: "date" covers both date-only and date+time use cases, controlled by the field_options.includeTime flag.
Field-definition shape¶
includeTime |
Stored value format | Description |
|---|---|---|
false (default) |
YYYY-MM-DD |
Date-only (legacy MetaVox behaviour) |
true |
YYYY-MM-DDTHH:mm:ss |
Floating ISO 8601 — no Z, no timezone offset |
field_options is an object for date fields (not a list, as it is for select/multiselect):
Pre-2.1.0 date fields with field_options = null or [] are treated as includeTime = false. When such a field is updated via the API without explicitly setting field_options, it normalises to {"includeTime": false} on save.
Value-format contract (writes)¶
When saving metadata via any of the three write endpoints, date values are validated server-side:
Field's includeTime |
Accepted regex | Example |
|---|---|---|
false |
^\d{4}-\d{2}-\d{2}$ |
"2026-04-15" |
true |
^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$ |
"2026-04-15T14:30:00" |
Empty string "" and JSON null are always allowed (clears the value).
Floating semantics — no timezone conversion. MetaVox never adds, strips, or converts timezones. A value of "2026-04-15T14:30:00" means "14:30 wherever the reader is" — same wall-clock everywhere. This matches SharePoint's behaviour for SPFieldDateTime columns that omit the Z.
Rejected forms (return 400):
- "2026-04-15T14:30:00Z" — explicit UTC suffix
- "2026-04-15T14:30:00+02:00" — explicit offset
- "2026-04-15T14:30" — missing seconds
- "2026-04-15 14:30:00" — space instead of T
- "15/04/2026" — locale-formatted dates
- Any cross-format mismatch (date-only value for includeTime=true field, or vice versa)
SharePoint migration mapping¶
SharePoint SPFieldDateTime.DisplayFormat |
MetaVox |
|---|---|
DateOnly (0) |
field_type: "date", field_options: {"includeTime": false} |
DateTime (1) |
field_type: "date", field_options: {"includeTime": true} |
CSV importer support for SharePoint datetime columns is planned for a future release.
Read path¶
GET endpoints return the stored value verbatim — no formatting, no timezone conversion:
{
"ocs": {
"data": {
"file_gf_meeting_start": "2026-05-13T14:30:00",
"file_gf_publication_date": "2026-04-15"
}
}
}
Lexicographic sort works correctly across both formats because ISO 8601 sorts chronologically by string comparison. No code-side adjustment needed when mixing date-only and datetime values in the same query.
Views¶
List views for a groupfolder¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/views
Response includes Cache-Control: private, max-age=600
Response:
{
"views": [
{
"id": 1,
"gf_id": 3,
"name": "WOO open",
"is_default": true,
"columns": [
{"field_name": "publicatiestatus", "visible": true, "filterable": true, "column_order": 0},
{"field_name": "verantwoordelijke", "visible": true, "filterable": false, "column_order": 1}
],
"filters": {"publicatiestatus": ["Open"]},
"sort_field": "publicatiestatus",
"sort_order": "ASC"
}
],
"can_manage": true
}
Create a view¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/views
Requires: admin or manage-fields permission
Request body:
{
"name": "WOO open",
"is_default": false,
"columns": [
{"field_name": "publicatiestatus", "visible": true, "filterable": true, "column_order": 0}
],
"filters": {"publicatiestatus": ["Open"]},
"sort_field": "publicatiestatus",
"sort_order": "ASC"
}
Update a view¶
Endpoint: PUT /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/views/{viewId}
Requires: admin or manage-fields permission
Body: same as create.
Delete a view¶
Endpoint: DELETE /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/views/{viewId}
Requires: admin or manage-fields permission
Reorder views¶
Endpoint: PUT /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/views/reorder
Requires: admin or manage-fields permission
Request body:
The order of the IDs determines the tab display order.
Column Configuration¶
Get column config (visible columns only)¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/columns
Response includes Cache-Control: private, max-age=600
Returns only fields with show_as_column = true, ordered by column_order.
Response:
[
{
"field_id": 4,
"field_name": "publicatiestatus",
"field_label": "Publicatiestatus",
"field_type": "select",
"field_options": ["Open", "Gesloten", "In behandeling"],
"show_as_column": true,
"column_order": 0,
"filterable": true
}
]
Set column config¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/columns
Requires: access to the groupfolder
Request body:
{"columns": [
{"field_id": 4, "show_as_column": true, "column_order": 0, "filterable": true},
{"field_id": 5, "show_as_column": true, "column_order": 1, "filterable": false}
]}
Get all filter values (batch)¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/all-filter-values
Response includes Cache-Control: private, max-age=300
Returns distinct values for all fields in one request. For select/multiselect/checkbox fields, returns configured options from field definition. For text/date/number fields, queries the database.
Optional parameter: field_names (comma-separated) to limit to specific fields.
Response:
{
"publicatiestatus": ["Open", "Gesloten", "In behandeling"],
"verantwoordelijke": ["j.doe", "a.jansen"],
"is_verwerkt": ["1", "0"]
}
Get scoped filter values (current directory)¶
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/filter-values
Returns distinct values scoped to specific file IDs (e.g., files in the current directory).
Request body:
Note: For checkbox fields, values are
"1"(checked) and"0"(unchecked). The frontend maps these to "Yes"/"No".
Get sorted and filtered file IDs¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/sorted-file-ids
Returns an ordered list of file IDs matching the given sort and filter criteria. Used for server-side sorting on large datasets.
Parameters:
- sort_field — field name to sort by
- sort_order — asc or desc (default: asc)
- sort_field_type — text, number, date, or checkbox (default: text)
- filters — JSON object of filter criteria, e.g. {"publicatiestatus": ["Open"]}
Response:
Get directory metadata (for file list columns)¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/groupfolders/{groupfolderId}/directory-metadata?file_ids=1,2,3
Returns metadata for up to 200 file IDs per request, limited to column-configured fields. Used by the file list to populate metadata columns.
Response:
{
"123": {"publicatiestatus": "Open", "verantwoordelijke": "j.doe"},
"456": {"publicatiestatus": "Gesloten"}
}
Batch Operations¶
1. Batch Update File Metadata¶
Update metadata for multiple files in one request.
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-update
Request Body:
{
"updates": [
{
"file_id": 290,
"groupfolder_id": 1,
"metadata": {
"file_gf_testfield": "test value",
"file_gf_status": "In Progress",
"file_gf_priority": "High"
}
},
{
"file_id": 628,
"groupfolder_id": 1,
"metadata": {
"file_gf_testfield": "another value",
"file_gf_status": "Completed",
"file_gf_priority": "Medium"
}
}
]
}
Response:
{
"ocs": {
"meta": {"status": "ok", "statuscode": 200},
"data": {
"success": true,
"total": 2,
"successful": 2,
"failed": 0,
"results": [
{"file_id": 290, "success": true, "fields_updated": 3},
{"file_id": 628, "success": true, "fields_updated": 3}
]
}
}
}
cURL Example:
curl -X POST "https://your-nextcloud.com/ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-update" \
-H "OCS-APIRequest: true" \
-H "Content-Type: application/json" \
-u "username:app-password" \
-d '{
"updates": [
{
"file_id": 290,
"groupfolder_id": 1,
"metadata": {
"file_gf_testfield": "test value",
"file_gf_status": "In Progress"
}
}
]
}'
2. Batch Delete File Metadata¶
Delete metadata from multiple files.
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-delete
Request Body (Delete All Metadata):
{
"deletes": [
{"file_id": 290, "groupfolder_id": 1, "field_names": null},
{"file_id": 628, "groupfolder_id": 1, "field_names": null}
]
}
Request Body (Delete Specific Fields):
{
"deletes": [
{"file_id": 290, "groupfolder_id": 1, "field_names": ["file_gf_testfield", "file_gf_status"]},
{"file_id": 628, "groupfolder_id": 1, "field_names": ["file_gf_testfield"]}
]
}
Response:
{
"ocs": {
"meta": {"status": "ok", "statuscode": 200},
"data": {
"success": true,
"total": 2,
"successful": 2,
"failed": 0,
"results": [
{"file_id": 290, "groupfolder_id": 1, "success": true, "fields_deleted": 2},
{"file_id": 628, "groupfolder_id": 1, "success": true, "fields_deleted": 1}
]
}
}
}
3. Batch Copy File Metadata¶
Copy metadata from one file to multiple files within the same groupfolder.
Endpoint: POST /ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-copy
Request Body (Copy All Fields):
{
"source_file_id": 290,
"source_groupfolder_id": 1,
"target_file_ids": [628, 789, 101],
"groupfolder_id": 1
}
Request Body (Copy Specific Fields):
{
"source_file_id": 290,
"source_groupfolder_id": 1,
"target_file_ids": [628, 789],
"groupfolder_id": 1,
"field_names": ["file_gf_testfield", "file_gf_status", "file_gf_priority"]
}
Response:
{
"ocs": {
"meta": {"status": "ok", "statuscode": 200},
"data": {
"success": true,
"source_file_id": 290,
"fields_copied": 3,
"target_results": [
{"file_id": 628, "success": true, "fields_updated": 3},
{"file_id": 789, "success": true, "fields_updated": 3},
{"file_id": 101, "success": true, "fields_updated": 3}
]
}
}
}
4. Get Metadata Statistics¶
Endpoint: GET /ocs/v2.php/apps/metavox/api/v1/metadata/statistics
Response:
{
"ocs": {
"meta": {"status": "ok", "statuscode": 200},
"data": {
"total_fields": 25,
"total_values": 1547,
"files_with_metadata": 423,
"fields_by_type": [
{"field_type": "text", "count": "15"},
{"field_type": "select", "count": "5"},
{"field_type": "date", "count": "3"},
{"field_type": "multi_select", "count": "2"}
]
}
}
}
Migration Workflow¶
Complete OCS-only workflow for programmatic setup — suitable for migrations from SharePoint or other systems. All steps use app password authentication; no browser session required.
Step 1 — Create fields (admin)¶
curl -X POST ".../ocs/v2.php/apps/metavox/api/v1/groupfolder-fields" \
-H "OCS-APIRequest: true" -H "Content-Type: application/json" \
-u "admin:app-password" \
-d '{"field_name": "publicatiestatus", "field_label": "Publicatiestatus", "field_type": "select", "field_options": ["Open", "Gesloten"]}'
Repeat for each field. Note the returned id for step 2.
Step 2 — Assign fields to groupfolder (admin)¶
curl -X POST ".../ocs/v2.php/apps/metavox/api/v1/groupfolders/3/fields" \
-H "OCS-APIRequest: true" -H "Content-Type: application/json" \
-u "admin:app-password" \
-d '{"field_ids": [4, 5, 6]}'
Step 3 — Set column config¶
curl -X POST ".../ocs/v2.php/apps/metavox/api/v1/groupfolders/3/columns" \
-H "OCS-APIRequest: true" -H "Content-Type: application/json" \
-u "admin:app-password" \
-d '{"columns": [{"field_id": 4, "show_as_column": true, "column_order": 0, "filterable": true}]}'
Step 4 — Create views¶
curl -X POST ".../ocs/v2.php/apps/metavox/api/v1/groupfolders/3/views" \
-H "OCS-APIRequest: true" -H "Content-Type: application/json" \
-u "admin:app-password" \
-d '{"name": "WOO open", "is_default": true, "columns": [{"field_name": "publicatiestatus", "visible": true, "filterable": true, "column_order": 0}], "filters": {"publicatiestatus": ["Open"]}, "sort_field": "publicatiestatus", "sort_order": "ASC"}'
Step 5 — Grant permissions (admin)¶
# Grant manage-fields to a group
curl -X POST ".../index.php/apps/metavox/api/permissions/group" \
-H "Content-Type: application/json" -b "session-cookie" \
-d '{"group_id": "woo-beheerders", "permission": "manage-fields", "groupfolder_id": 3}'
Note: Permission endpoints are on traditional routes (require session or admin). App password works if the user is a Nextcloud admin.
Step 6 — Import metadata in batches of ≤100 files¶
curl -X POST ".../ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-update" \
-H "OCS-APIRequest: true" -H "Content-Type: application/json" \
-u "admin:app-password" \
-d '{"updates": [{"file_id": 290, "groupfolder_id": 3, "metadata": {"file_gf_publicatiestatus": "Open"}}]}'
For large datasets (10 000+ files), paginate in batches of 100 on the client side.
Step 7 — Trigger backup (optional)¶
Error Handling¶
All batch operations return detailed error information per item:
{
"ocs": {
"meta": {"status": "ok", "statuscode": 200},
"data": {
"success": true,
"total": 3,
"successful": 2,
"failed": 1,
"results": [
{"file_id": 123, "success": true, "fields_updated": 3},
{"file_id": 456, "success": false, "error": "File not found"},
{"file_id": 789, "success": true, "fields_updated": 3}
]
}
}
}
Best Practices¶
- Batch Size: Limit batches to 100 items per request for optimal performance
- Transaction Safety: All batch operations use database transactions
- Error Handling: Check individual results for partial failures
- Field Names: Use the exact field names as defined in MetaVox
- Authentication: Use app passwords for external scripts — no session or CSRF token needed
JavaScript/TypeScript Example¶
// Batch update example
async function batchUpdateMetadata(files, groupfolderId) {
const updates = files.map(file => ({
file_id: file.id,
groupfolder_id: groupfolderId,
metadata: {
file_gf_testfield: file.testValue,
file_gf_status: file.status,
file_gf_priority: file.priority
}
}));
const response = await fetch(
'/ocs/v2.php/apps/metavox/api/v1/files/metadata/batch-update',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'OCS-APIRequest': 'true'
},
body: JSON.stringify({ updates })
}
);
const result = await response.json();
if (result.ocs.data.failed > 0) {
console.error('Some updates failed:', result.ocs.data.results);
}
return result.ocs.data;
}
Summary¶
OCS API (app password / OAuth — no CSRF)¶
| Area | Endpoints |
|---|---|
| Fields | Create, update, delete fields; assign to groupfolder; list file-level fields |
| File Metadata | Read/write per file; bulk read (≤100); groupfolder-scoped read/write |
| Groupfolders | List, read/write folder-level metadata |
| Views | List, create, update, delete, reorder views per groupfolder |
| Column config | Get/set visible columns and filter config |
| Batch operations | Batch update, delete, copy metadata (≤100 files/request) |
| Filter values | All values (batch), scoped values, sorted file IDs |
| Directory metadata | Bulk metadata for file list columns (≤200 files) |
| Statistics | Metadata usage stats |
Browser API (session + CSRF)¶
| Area | Endpoints |
|---|---|
| Initialization | Single-call bootstrap (groupfolders + fields + views) |
| User endpoints | Non-admin groupfolder/field/metadata access |
| Cell locking | Lock/unlock cells for concurrent editing |
| Presence | Track active users per groupfolder |
| AI Autofill | AI-powered metadata generation |
| Backup & Restore | Create, list, download, restore metadata backups |
| Settings | Admin app settings (AI toggle) |
| Telemetry | Anonymous usage reporting |
| Permissions | Granular permission management (user/group) |
| Utilities | User search, metadata export |
| Cell locking | Lock/unlock cells for concurrent editing |
| Presence | Track active users per groupfolder |
| AI Autofill | AI-powered metadata generation |
| Backup & Restore | Create and restore metadata backups |
| Settings | Admin app settings |
| Telemetry | Anonymous usage reporting |
| Permissions | Granular permission management |
Cell Locking (Real-Time Collaboration)¶
Requires notify_push and Redis for full functionality.
Lock a cell¶
Endpoint: POST /apps/metavox/api/groupfolders/{groupfolderId}/files/{fileId}/lock
Request body:
Response (success):
Response (already locked by another user, HTTP 409):
Unlock a cell¶
Endpoint: POST /apps/metavox/api/groupfolders/{groupfolderId}/files/{fileId}/unlock
Request body:
Presence leave¶
Called via sendBeacon on tab close to clean up presence tracking.
Endpoint: POST /apps/metavox/api/presence/leave
AI Autofill¶
Requires a Nextcloud AI task processing provider (e.g., the LLM2 app).
Check AI availability¶
Endpoint: GET /apps/metavox/api/ai/status
Response:
Generate metadata suggestions¶
Endpoint: POST /apps/metavox/api/ai/generate
Request body:
Response:
Backup & Restore¶
Admin-only endpoints for metadata backup and restore.
List backups¶
Endpoint: GET /apps/metavox/api/backup/list
Trigger backup¶
Endpoint: POST /apps/metavox/api/backup/trigger
Creates a gzip-compressed JSON backup of all metadata tables. Maximum 7 backups are retained.
Restore backup¶
Endpoint: POST /apps/metavox/api/backup/restore
Request body:
Download backup¶
Endpoint: GET /apps/metavox/api/backup/download?filename=metavox_backup_2026-03-24_120000.json.gz
Backup status¶
Endpoint: GET /apps/metavox/api/backup/status
Returns the current status of a running backup or restore operation. Used by the frontend to show progress.
Response:
Settings¶
Admin-only endpoints for app configuration.
Get settings¶
Endpoint: GET /apps/metavox/api/settings
Requires: Nextcloud admin
Response:
Save settings¶
Endpoint: POST /apps/metavox/api/settings
Requires: Nextcloud admin
Request body:
Telemetry¶
See Telemetry for details.
| Endpoint | Method | Description |
|---|---|---|
/apps/metavox/api/telemetry/status |
GET | Check if telemetry is enabled |
/apps/metavox/api/telemetry/stats |
GET | View collected statistics |
/apps/metavox/api/telemetry/send |
POST | Manually trigger telemetry report |
/apps/metavox/api/telemetry/settings |
POST | Enable or disable telemetry |
Permissions¶
See Permissions for details.
| Endpoint | Method | Description |
|---|---|---|
/apps/metavox/api/permissions |
GET | List all permissions |
/apps/metavox/api/permissions/me |
GET | Check your own permissions |
/apps/metavox/api/permissions/check |
GET | Check a specific permission |
/apps/metavox/api/permissions/groups |
GET | List available groups |
/apps/metavox/api/permissions/user |
POST | Grant permission to a user |
/apps/metavox/api/permissions/group |
POST | Grant permission to a group |
/apps/metavox/api/permissions/user/{id} |
DELETE | Revoke user permission |
/apps/metavox/api/permissions/group/{id} |
DELETE | Revoke group permission |
Initialization¶
Bootstrap endpoint¶
Endpoint: GET /apps/metavox/api/init
Single-call initialization for the files plugin. Returns all data needed to render metadata columns in one request, eliminating multiple sequential API calls.
Parameters:
- dir — current directory path (used to detect groupfolder)
Response:
{
"groupfolders": [
{"id": 3, "mount_point": "Shared Documents", ...}
],
"groupfolder_id": 3,
"fields": [
{"field_name": "publicatiestatus", "field_label": "Publicatiestatus", "field_type": "select", ...}
],
"views": [
{"id": 1, "name": "WOO open", "is_default": true, "columns": [...], ...}
],
"can_manage": true
}
Note: This endpoint also registers presence for the detected groupfolder (for real-time sync).
User Endpoints (Browser Routes)¶
Non-admin endpoints for users to manage their accessible groupfolders and metadata. These require a browser session.
| Endpoint | Method | Description |
|---|---|---|
/apps/metavox/api/user/groupfolders |
GET | List groupfolders accessible by current user |
/apps/metavox/api/user/groupfolder-fields |
GET | Get all available field definitions |
/apps/metavox/api/user/groupfolders/{id}/fields |
GET | Get assigned fields for a groupfolder |
/apps/metavox/api/user/groupfolders/{id}/fields |
POST | Assign fields to a groupfolder |
/apps/metavox/api/user/groupfolders/{id}/metadata |
GET | Get groupfolder metadata |
/apps/metavox/api/user/groupfolders/{id}/metadata |
POST | Save groupfolder metadata |
Utility Endpoints (Browser Routes)¶
Search users¶
Endpoint: GET /apps/metavox/api/users?search={query}
Returns up to 25 matching Nextcloud users. Used by the user field type for autocomplete.
Response:
Export metadata to CSV¶
Endpoint: POST /apps/metavox/api/files/export-metadata
Exports metadata for selected files as a JSON array (client formats to CSV).
Request body: