# SDK for Java (sdk4j) - OpenKM
> This document is intended for LLM consumption. It consolidates the complete documentation for sdk4j, the official Java SDK for the OpenKM REST API.
> Source: https://docs.openkm.com/kcenter/view/sdk4j-5.4/
> Compatibility: sdk4j 5.4 requires OpenKM Professional 8.2.4 or newer.
---
## Overview
The OpenKM SDK for Java (sdk4j) is a comprehensive toolkit enabling developers to build applications integrated with OpenKM. It wraps the full OpenKM REST API, providing type-safe Java objects, authentication handling, and broad compatibility across OpenKM REST versions while minimising code modifications.
**License**: EULA - OpenKM SDK End User License Agreement, published by OpenKM Knowledge Management System S.L.
---
## Maven Setup
Add the OpenKM Maven repository and the sdk4j dependency to your `pom.xml`:
```xml
openkm.com
OpenKM Maven Repository
https://maven.openkm.com
com.openkm
sdk4j
5.4
```
### Packaging: maven-shade-plugin
When building an executable JAR, use `maven-shade-plugin` (not `maven-assembly-plugin`) to avoid a "missing MessageBodyWriter" error. Include the following transformer:
```xml
META-INF/services/javax.ws.rs.ext.MessageBodyWriter
```
---
## Package Structure
| Package | Description |
|---------|-------------|
| `com.openkm.sdk4j` | Factory class `OKMWebservicesFactory` that returns an `OKMWebservices` instance |
| `com.openkm.sdk4j.bean` | Data transfer objects (POJOs) — unmarshalled REST response objects |
| `com.openkm.sdk4j.definition` | Interface definitions (`BaseActivity`, `BaseAuth`, etc.) |
| `com.openkm.sdk4j.impl` | Concrete implementations (`ActivityImpl`, `AuthImpl`, etc.) |
| `com.openkm.sdk4j.util` | Utilities, including ISO8601 date parsing |
| `com.openkm.sdk4j.exception` | Exception classes |
---
## Initialization and Authentication
### Creating a client instance
```java
import com.openkm.sdk4j.OKMWebservicesFactory;
import com.openkm.sdk4j.impl.OKMWebservices;
String host = "http://localhost:8080/openkm";
OKMWebservices ws = OKMWebservicesFactory.getInstance(host);
```
`OKMWebservicesFactory.getInstance(String host)` is a static factory method that creates and returns a new `OKMWebservices` object configured for the given host URL.
### Login with username and password
```java
ws.login(user, password);
```
### Login with username, password, expiration and IP restriction
```java
ws.login(user, password, expiration, restrictIp);
```
### Login with a Personal Access Token
```java
ws.login(personalAccessToken);
```
All `login()` variants obtain an authorization token from the server and automatically propagate it to every service implementation inside the `OKMWebservices` object. The token is stored internally; you do not need to manage it manually.
### Logout
```java
ws.logout();
```
Invalidates the session and resets the internal authorization token to `null`.
### Refresh token
```java
String newToken = ws.getRefreshToken();
```
Retrieves a refreshed token and updates all service implementations automatically.
### Setting authorization token manually
```java
ws.setAuthorizationToken(authorizationToken);
```
Useful when reusing a previously obtained token across sessions.
---
## OKMWebservices — Service Registry
`OKMWebservices` is the central object. It holds one public field per service area. All fields are initialized in the constructor.
```java
public class OKMWebservices implements Serializable {
public AuthImpl auth;
public CronTabImpl cronTab;
public FolderImpl folder;
public PropertyGroupImpl propertyGroup;
public RepositoryImpl repository;
public ActivityImpl activity;
public BookmarkImpl bookmark;
public ConversionImpl conversion;
public DashboardImpl dashboard;
public ImportImpl quickImport;
public NoteImpl note;
public NotificationImpl notification;
public PropertyImpl property;
public ShardImpl shard;
public RecordImpl record;
public NodeImpl node;
public PdfImpl pdf;
public PluginImpl plugin;
public ReportImpl report;
public UserConfigImpl userConfig;
public StampImpl stamp;
public TaskImpl task;
public RelationImpl relation;
public AutomationImpl automation;
public OcrImpl ocr;
public SearchImpl search;
public DocumentImpl document;
public MailImpl mail;
public WorkflowImpl workflow;
public WizardImpl wizard;
}
```
Access any service directly via the field, for example:
```java
ws.document.getProperties(uuid);
ws.folder.getChildren(parentUuid);
ws.auth.getGrantedUsers(nodeUuid);
```
---
## Service Areas
The following table lists all service areas exposed by the SDK:
| Field | Interface | Description |
|-------|-----------|-------------|
| `ws.activity` | `BaseActivity` | Activity log management |
| `ws.auth` | `BaseAuth` | Security, users, roles, grants |
| `ws.automation` | `BaseAutomation` | Automation rules management |
| `ws.bookmark` | `BaseBookmark` | User bookmarks |
| `ws.conversion` | `BaseConversion` | Document format conversion |
| `ws.cronTab` | `BaseCronTab` | Scheduled tasks (cron) |
| `ws.dashboard` | `BaseDashboard` | Dashboard data retrieval |
| `ws.document` | `BaseDocument` | Document CRUD, locking, versioning |
| `ws.folder` | `BaseFolder` | Folder management |
| `ws.quickImport` | `BaseImport` | Bulk import capabilities |
| `ws.mail` | `BaseMail` | Email/mail node management |
| `ws.node` | `BaseNode` | Generic node operations |
| `ws.note` | `BaseNote` | Notes attached to nodes |
| `ws.notification` | `BaseNotification` | Subscription and notification management |
| `ws.ocr` | `BaseOcr` | OCR processing |
| `ws.pdf` | `BasePdf` | PDF operations |
| `ws.plugin` | `BasePlugin` | Plugin management |
| `ws.property` | `BaseProperty` | Categories and keywords |
| `ws.propertyGroup` | `BasePropertyGroup` | Metadata / property groups |
| `ws.record` | `BaseRecord` | Records management |
| `ws.relation` | `BaseRelation` | Node relationships |
| `ws.report` | `BaseReport` | Report execution |
| `ws.repository` | `BaseRepository` | Repository-level properties and info |
| `ws.search` | `BaseSearch` | Full-text and parametric search |
| `ws.shard` | `BaseShard` | Shard management |
| `ws.stamp` | `BaseStamp` | Stamp management |
| `ws.task` | `BaseTask` | Task creation and tracking |
| `ws.userConfig` | `BaseUserConfig` | User configuration |
| `ws.wizard` | `BaseWizard` | Wizard actions |
| `ws.workflow` | `BaseWorkflow` | Workflow process execution |
---
## Quick Start Example
```java
package com.openkm;
import com.openkm.sdk4j.OKMWebservicesFactory;
import com.openkm.sdk4j.bean.Folder;
import com.openkm.sdk4j.impl.OKMWebservices;
public class QuickStart {
public static void main(String[] args) {
String host = "http://localhost:8080/openkm";
String user = "okmAdmin";
String password = "admin";
OKMWebservices ws = OKMWebservicesFactory.getInstance(host);
try {
ws.login(user, password);
// List children folders of a node by UUID
for (Folder fld : ws.folder.getChildren("4f873d10-654e-4d99-a94f-15466e30a0f6")) {
System.out.println("Folder -> " + fld.getPath());
}
// Get repository version info
System.out.println(ws.repository.getAppVersion());
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
---
---
## ws.activity — Activity Log
Activity logs track system events across documents, folders, mail items, and records.
### findActivityLog
```java
ActivityList findActivityLog(int page, int length, Calendar beginDate,
Calendar endDate, String user, String action, String item)
```
Retrieves activity log entries filtered by date range, user, action type, and node.
| Parameter | Type | Description |
|-----------|------|-------------|
| `page` | int | Page number for pagination (0-based) |
| `length` | int | Number of records per page |
| `beginDate` | Calendar | Start date filter |
| `endDate` | Calendar | End date filter |
| `user` | String | Username filter |
| `action` | String | Action type filter; empty string returns all actions |
| `item` | String | UUID of the node (document, folder, mail, or record) |
**Returns:** `ActivityList` — iterable via `getActivities()`
```java
ws.login(user, password);
Calendar beginDate = Calendar.getInstance();
beginDate.add(Calendar.MONTH, -1);
Calendar endDate = Calendar.getInstance();
String item = "f84a2e1f-a858-4e53-9c09-36519d903782";
ActivityList results = ws.activity.findActivityLog(0, 20, beginDate, endDate, user, "", item);
for (Activity activity : results.getActivities()) {
System.out.println(activity);
}
```
### getActivityActions
```java
List getActivityActions()
```
Returns all valid activity action type identifiers supported by the system.
```java
for (String action : ws.activity.getActivityActions()) {
System.out.println(action);
}
```
---
## ws.auth — Authentication & Authorization
### Permission constants
`com.openkm.sdk4j.bean.Permission` provides permission bit constants. Combine with `+` or `|`:
```java
int permission = Permission.READ + Permission.WRITE;
// Check:
if ((permission | Permission.WRITE) == Permission.WRITE) { /* has WRITE */ }
```
### Authentication
| Method | Description |
|--------|-------------|
| `ws.login(user, password)` | Login; token valid 24 h by default |
| `ws.login(user, password, expiration, restrictIP)` | Login with custom expiration (days) and optional IP lock |
| `ws.setAuthorizationToken(token)` | Reuse a previously obtained token |
| `ws.logout()` | Server-side logout; resets internal token |
| `ws.getRefreshToken()` | Refresh current token before expiration |
| `ws.auth.setUserTenant(tenantId)` | Switch tenant in multi-tenant environments |
| `ws.auth.getToken()` | Generate a fresh token (resets 24 h expiration) |
> **Note:** The first successful `login()` creates the user's folder structure (`/okm:trash/userId`). Without it, path-based calls will throw `PathNotExistsException`.
### User management
```java
// List all users (including disabled)
List users = ws.auth.getUsers(true);
// Get a single user
CommonUser u = ws.auth.getUser("okmAdmin");
// Users by role
List admins = ws.auth.getUsersByRole("ROLE_ADMIN");
// Create user
ws.auth.createUser("test", "password.2016", "test@mail.com", "User test", true);
// Update user
ws.auth.updateUser("test", "newpassword", "test@mail.com", "Test", false);
// Delete user
ws.auth.deleteUser("test");
// Get token for a specific user
String token = ws.auth.getUserToken("okmAdmin");
```
### Role management
```java
// List all roles
List roles = ws.auth.getRoles(true);
// Roles assigned to a user
List userRoles = ws.auth.getRolesByUser("okmAdmin");
// Create / update / delete role
ws.auth.createRole("ROLE_TEST", true);
ws.auth.updateRole("ROLE_TEST", true);
ws.auth.deleteRole("ROLE_TEST");
// Assign / remove role from user
ws.auth.assignRole("test", "ROLE_USER");
ws.auth.removeRole("test", "ROLE_USER");
```
### Node permissions (grants)
```java
// Read grants on a node
GrantedUsersAndRolesItem grants = ws.auth.getGrantedUsersAndRoles(uuid);
Map userGrants = ws.auth.getGrantedUsers(uuid);
Map roleGrants = ws.auth.getGrantedRoles(uuid);
// Grant permissions
ws.auth.grantUser(uuid, "sochoa", Permission.ALL_GRANTS, false);
ws.auth.grantRole(uuid, "ROLE_USER", Permission.ALL_GRANTS, true); // recursive
// Revoke permissions
ws.auth.revokeUser(uuid, "sochoa", Permission.ALL_GRANTS, false);
ws.auth.revokeRole(uuid, "ROLE_USER", Permission.ALL_GRANTS, false);
// Set (overwrite) permissions for a user or role on a node
ws.auth.setUserPermissions(uuid, "sochoa", Permission.READ + Permission.WRITE, false);
ws.auth.setRolePermissions(uuid, "ROLE_USER", Permission.READ + Permission.WRITE, false);
```
> The `recursive` flag only applies to folders and record nodes.
### Batch security changes
**`changeSecurity`** — adds grants (does not remove existing ones):
```java
List guList = new ArrayList<>();
GrantedUser gu = new GrantedUser();
gu.setUser("sochoa");
gu.setPermissions(Permission.ALL_GRANTS);
guList.add(gu);
List grList = new ArrayList<>();
GrantedRole gr = new GrantedRole();
gr.setRole("ROLE_TEST");
gr.setPermissions(Permission.READ | Permission.WRITE);
grList.add(gr);
ChangeSecurity cs = new ChangeSecurity();
cs.setGrantedUsersList(guList);
cs.setGrantedRolesList(grList);
cs.setRecursive(true);
ws.auth.changeSecurity(uuid, cs);
```
**`overwriteSecurity`** — replaces all grants completely:
```java
ChangeSecurity cs = new ChangeSecurity();
cs.setGrantedUsersList(guList);
cs.setRecursive(true);
ws.auth.overwriteSecurity(uuid, cs);
```
### Profiles
```java
// List active profiles
for (Profile p : ws.auth.getProfiles(true)) {
System.out.println(p.getName());
}
// Get profile of a user
Profile p = ws.auth.getUserProfile("okmAdmin");
// Assign profile to user
ws.auth.setUserProfile("test", profile.getId());
```
### Tenants
```java
List tenants = ws.auth.getUserTenants();
ws.auth.setUserTenant(tenantId);
```
### Personal Access Tokens
```java
// Create long-lived token with expiration date
Calendar expiration = Calendar.getInstance();
expiration.set(Calendar.DAY_OF_MONTH, 10);
String pat = ws.auth.createPersonalAccessToken("openkm", expiration);
```
### Misc
```java
String sessionId = ws.auth.getSessionId();
boolean isLowercase = ws.auth.isLoginLowercase();
```
---
## ws.bookmark — Bookmarks
Bookmarks allow users to save references to nodes (documents, folders, mails, records) for quick access.
### getUserBookmarks
```java
List getUserBookmarks()
```
Returns all bookmarks of the currently authenticated user.
```java
for (Bookmark bookmark : ws.bookmark.getUserBookmarks()) {
System.out.println(bookmark);
}
```
### create
```java
void create(String uuid, String name)
```
Creates a bookmark for the node identified by `uuid`.
```java
ws.bookmark.create("a37f570f-5e7f-4a03-8d04-9b3689be82f1", "test bookmark");
```
### rename
```java
void rename(int bookmarkId, String name)
```
Renames an existing bookmark.
```java
ws.bookmark.rename(1, "set bookmark");
```
### delete
```java
void delete(int bookmarkId)
```
Removes a bookmark by its ID.
```java
ws.bookmark.delete(1);
```
---
---
## ws.conversion — Document & Image Conversion
All conversion methods stream content in and out via `InputStream`. Use `IOUtils.copy()` (Apache Commons IO) for stream handling and always close streams after use.
> **Server requirements:** `doc2pdf` requires OpenOffice/LibreOffice enabled. `imageConvert` requires ImageMagick. `html2pdf` requires an HTML-to-PDF tool. `img2txt` requires an OCR engine. `barcode2txt` requires barcode tools.
### doc2pdf
```java
InputStream doc2pdf(InputStream is, String fileName)
```
Converts a document to PDF. The `fileName` is used to detect the MIME type by extension (e.g. `"report.docx"`). Returns an `InputStream` with the PDF content.
```java
InputStream pdfStream = ws.conversion.doc2pdf(new FileInputStream("report.docx"), "report.docx");
IOUtils.copy(pdfStream, new FileOutputStream("report.pdf"));
pdfStream.close();
```
### imageConvert
```java
InputStream imageConvert(InputStream is, String fileName, String params, String dstMimeType)
```
Applies an ImageMagick transformation to an image.
| Parameter | Description |
|-----------|-------------|
| `is` | Source image stream |
| `fileName` | Original filename (for MIME detection) |
| `params` | ImageMagick parameters — **must end with `${fileIn} ${fileOut}`**, use single spaces |
| `dstMimeType` | Output MIME type (e.g. `"image/png"`) |
```java
InputStream result = ws.conversion.imageConvert(
new FileInputStream("photo.jpg"), "photo.jpg",
"-resize 800x600 ${fileIn} ${fileOut}", "image/jpeg");
```
> Tip: test your ImageMagick `params` locally before integrating.
### html2pdf
```java
InputStream html2pdf(String url)
```
Converts a public URL to a PDF document. Returns the PDF as an `InputStream`.
```java
InputStream pdf = ws.conversion.html2pdf("https://www.example.com");
IOUtils.copy(pdf, new FileOutputStream("page.pdf"));
pdf.close();
```
### doc2txt
```java
String doc2txt(InputStream is, String fileName)
```
Extracts plain text from a document. Requires an appropriate text extractor configured for the document's MIME type.
```java
String text = ws.conversion.doc2txt(new FileInputStream("report.pdf"), "report.pdf");
System.out.println(text);
```
### img2txt
```java
String img2txt(InputStream is, String fileName)
```
Performs OCR on an image and returns the recognised text.
```java
String text = ws.conversion.img2txt(new FileInputStream("scan.png"), "scan.png");
```
### barcode2txt
```java
String barcode2txt(InputStream is, String fileName)
```
Decodes a barcode from an image and returns the decoded value as a String.
```java
String barcode = ws.conversion.barcode2txt(new FileInputStream("barcode.png"), "barcode.png");
```
---
## ws.dashboard — Dashboard
All dashboard methods are paginated via `offset` and `limit` parameters and return a `DashboardResultSet` (except `getUserSearches` and `findUserSearches`).
**Pagination pattern:**
- `offset=0, limit=10` → first 10 results
- `offset=10, limit=10` → results 11–20
**Return types:**
- `DashboardResultSet` — contains total count + list of `DashboardResult` objects
- `List` — list of saved search parameter objects
- `List` — list of node results from a saved search
### Method reference
| Method | Description |
|--------|-------------|
| `getUserCheckedOutDocuments(offset, limit)` | Documents currently checked out by the user |
| `getUserLockedDocuments(offset, limit)` | Documents locked by the user |
| `getUserLastModifiedDocuments(offset, limit)` | Documents most recently modified by the user |
| `getUserLastDownloadedDocuments(offset, limit)` | Documents most recently downloaded |
| `getUserLastCreatedDocuments(offset, limit)` | Documents most recently created |
| `getUserLastDocumentsNotesCreated(offset, limit)` | Notes recently added to documents |
| `getUserSubscribedDocuments(offset, limit)` | Documents the user is subscribed to |
| `getUserLockedFolders(offset, limit)` | Folders locked by the user |
| `getUserSubscribedFolders(offset, limit)` | Folders the user is subscribed to |
| `getUserLastCreatedFolders(offset, limit)` | Folders recently created by the user |
| `getUserLastFoldersNotesCreated(offset, limit)` | Notes recently added to folders |
| `getUserLockedRecords(offset, limit)` | Records locked by the user |
| `getUserSubscribedRecords(offset, limit)` | Records the user is subscribed to |
| `getUserLastCreatedRecords(offset, limit)` | Records recently created |
| `getUserLastRecordsNotesCreated(offset, limit)` | Notes recently added to records |
| `getUserLockedMails(offset, limit)` | Mails locked by the user |
| `getUserLastImportedMails(offset, limit)` | Recently imported mail messages |
| `getUserLastMailsNotesCreated(offset, limit)` | Notes recently added to mails |
| `getUserLastImportedMailAttachments(offset, limit)` | Mails with attachments recently imported |
| `getUserSearches()` | Returns `List` of saved user searches |
| `findUserSearches(long qpId)` | Executes a saved search by ID; returns `List` |
```java
// Example: list the last 10 created documents
DashboardResultSet result = ws.dashboard.getUserLastCreatedDocuments(0, 10);
System.out.println("Total: " + result.getTotal());
for (DashboardResult item : result.getResults()) {
System.out.println(item);
}
// Example: run a saved search
List searches = ws.dashboard.getUserSearches();
for (QueryParams qp : searches) {
List nodes = ws.dashboard.findUserSearches(qp.getId());
nodes.forEach(System.out::println);
}
```
---
## ws.document — Documents
The most comprehensive service in the SDK. Covers the full lifecycle of a document: creation, content management, versioning, metadata, security, PDF operations, OCR, and more.
### Creation
```java
Document create(String uuid, File file)
Document create(String uuid, String name, InputStream is)
Document create(String uuid, String name, InputStream is, long nodeClass)
```
- `uuid` — UUID of the parent folder or record node
- `nodeClass` — business type (series) ID; `0` = none
- Returns a `Document` object with all properties of the created document
```java
// From a File
Document doc = ws.document.create(folderUuid, new File("/tmp/report.pdf"));
// From an InputStream with a name
Document doc = ws.document.create(folderUuid, "report.pdf", new FileInputStream("/tmp/report.pdf"));
```
### Reading content & properties
```java
Document getProperties(String uuid)
InputStream getContent(String uuid)
InputStream getContentByVersion(String uuid, String versionName)
String getExtractedText(String uuid)
InputStream getThumbnail(String uuid, ThumbnailType type)
List getChildren(String uuid) // children of a folder/record
List getCheckedOut() // all docs checked out by current user
int getNumberOfPages(String uuid)
String getPageAsImage(String uuid, int pageNumber, int maxWidth, int maxHeight) // Base64
```
> Use `getProperties()` to get the content length before streaming; do not rely on `InputStream.available()`.
`ThumbnailType` values: `THUMBNAIL_PROPERTIES`, `THUMBNAIL_LIGHTBOX`, `THUMBNAIL_SEARCH`.
### Metadata
```java
void setTitle(String uuid, String title)
void setDescription(String uuid, String description)
void setLanguage(String uuid, String lang) // ISO 639-1 code
void setExtractedText(String uuid, InputStream is)
void setNodeClass(String uuid, long nodeClass)
void setDispositionStage(String uuid, long stage)
void setIndexable(String uuid, boolean enabled)
List getDetectedLanguages()
```
### Rename, move, copy, delete
```java
Document rename(String uuid, String newName)
void move(String uuid, String dstId)
Document copy(String uuid, String dstId, String newName)
void delete(String uuid) // moves to /okm:trash/userId
void purge(String uuid) // permanent deletion — no recovery without backup
```
**`extendedCopy`** — copy with selective metadata transfer (use `ws.node.extendedCopy`):
```java
// copies categories, keywords, propertyGroups, notes, and security grants
ws.node.extendedCopy(nodeId, dstId, name, true, true, true, true, true);
```
### Version control
```java
void checkout(String uuid)
void cancelCheckout(String uuid) // only by the user who checked out
void forceCancelCheckout(String uuid) // requires ROLE_ADMIN
boolean isCheckedOut(String uuid)
Version checkin(String uuid, InputStream is, String comment)
Version checkin(String uuid, InputStream is, String comment, int increment)
long getVersionHistorySize(String uuid) // bytes used by version history
```
- Only the user who performed `checkout` can `checkin` or `cancelCheckout`.
- `increment` ≥ 1 depends on the configured `VersionNumberAdapter`.
### Annotations & differences
```java
String getAnnotations(String uuid, String versionName)
InputStream getDifferences(String uuid, String versionName1, String versionName2) // PDF
```
### PDF operations
```java
String mergePdf(String dstId, String docName, List uuids) // returns UUID of merged doc
InputStream getPdf(String uuid)
Document saveAsPdf(String uuid, String newName, boolean propertyGroups, boolean security)
boolean isConvertibleToPDF(String uuid) // false if already PDF
void webPageImport(String uuid, String url) // saves web page as PDF into repository
```
### OCR / data capture
```java
boolean isOCRDataCaptureSupported(String uuid)
OCRRecognise recognize(String uuid)
void captureData(String uuid, long templateId)
```
### Stamps
```java
void stamp(String uuid, int type, long stampId)
```
### Templates
```java
WizardNode createWizard(String uuid, String name, long nodeClass, InputStream is)
void updateFromTemplate(String uuid, String dstId, Map properties)
```
`createWizard` returns a `WizardNode` describing pending wizard actions (keyword assignment, category selection, metadata entry). Property values in the `properties` map must use ISO 8601 date format for date fields.
### Live Edit
```java
void liveEditCheckin(String uuid, String comment, int increment)
void liveEditCancelCheckout(String uuid)
void liveEditForceCancelCheckout(String uuid) // requires ROLE_ADMIN
void liveEditSetContent(String uuid, InputStream is)
```
### AutoCAD (XRef)
```java
List getXrefs(String uuid)
void refresh(String uuid)
```
### Utility / validation
```java
boolean isValid(String uuid) // true if node is a document
boolean isAttachment(String uuid) // true if node is a mail attachment
String getPath(String uuid) // converts UUID to repository path
```
---
---
## ws.filePlan — File Plan
The file plan organises records management through a hierarchy of **sections** and **classes** (node classes / business types). All methods are accessed via `ws.filePlan`.
**Key beans:**
- `NodeClassSectionDefinition` — an organisational section within the file plan
- `NodeClass` — a classification category (business type) for records
### Sections
```java
// Root sections
List getRootSections()
List getRootSectionsFilteredBySecurity(int permissions)
List getRootSectionsIdWithChildren()
List getAllSectionsOrdered()
// Children sections
List getChildrenSections(long parentId)
List getChildrenSectionsFilteredBySecurity(long parentId, int permissions)
List getChildrenSectionsIdByTenantWithChildren(long parentId)
// Search & navigation
List findSectionFiltered(String code, String name, int permissions)
List getBreadcrumb(long sectionId) // path from root to section
```
### Classes (Node Classes)
```java
List getChildrenClasses(long sectionId)
List getChildrenClassesFilteredBySecurity(long sectionId, int permissions)
NodeClass findNodeClassByPk(long id)
List getAllNodeClasses()
List findElectronicRecordClasses()
List findElectronicRecordClassesFilteredBySecurity(int permissions)
List findFilteredByCodeOrNameFilteredBySecurity(String code, String name, int permissions)
```
**Permissions** use constants from `com.openkm.sdk4j.bean.Permission` (`READ`, `WRITE`, `ALL_GRANTS`, etc.), combined with `+` or `|`.
```java
// Example: list root sections the current user can read
List roots =
ws.filePlan.getRootSectionsFilteredBySecurity(Permission.READ);
for (NodeClassSectionDefinition s : roots) {
System.out.println(s);
}
// Example: find all electronic record classes
List classes = ws.filePlan.findElectronicRecordClasses();
// Example: search sections by code prefix
List results =
ws.filePlan.findSectionFiltered("1.", "invoice", Permission.ALL_GRANTS);
// Example: get breadcrumb path to a section
List breadcrumb = ws.filePlan.getBreadcrumb(3L);
```
---
## ws.folder — Folders
### Creation
```java
Folder create(String uuid, String name)
Folder create(String uuid, String name, long nodeClass)
```
- `uuid` — UUID of the parent folder or record node
- `nodeClass` — business type (series) ID; `0` = none
- Returns a `Folder` object with all properties of the created folder
```java
Folder folder = ws.folder.create("1be884f4-5758-4985-94d1-f18bfe004db8", "test");
```
### Reading
```java
Folder getProperties(String uuid)
List getChildren(String uuid)
ContentInfo getContentInfo(String uuid) // folder count, doc count, record count, mail count, total bytes
boolean isValid(String uuid) // true if node is a folder
String getPath(String uuid) // UUID → repository path
```
```java
for (Folder fld : ws.folder.getChildren(parentUuid)) {
System.out.println(fld.getPath());
}
ContentInfo info = ws.folder.getContentInfo(uuid);
// info.getFolders(), info.getDocuments(), info.getRecords(), info.getMails(), info.getSize()
```
### Rename, move, copy, delete
```java
Folder rename(String uuid, String newName)
void move(String uuid, String dstId)
Folder copy(String uuid, String dstId, String newName) // null newName keeps original name
Folder extendedCopy(String uuid, String dstId, String newName,
boolean categories, boolean keywords,
boolean propertyGroups, boolean notes, boolean security)
void delete(String uuid) // moves to /okm:trash/userId
void purge(String uuid) // permanent — only recoverable from backup
```
> `copy` transfers only security grants. Use `extendedCopy` to also copy categories, keywords, property groups, notes, and/or security.
```java
// Simple copy, keep original name
ws.folder.copy(srcUuid, dstUuid, null);
// Extended copy with full metadata
ws.folder.extendedCopy(srcUuid, dstUuid, "new name folder", true, true, true, true, true);
```
### Metadata
```java
void setDescription(String uuid, String description)
void createMissingFolders(String fldPath) // creates all missing folders in the given path
```
```java
ws.folder.createMissingFolders("/okm:root/projects/2024/q1");
```
### Templates
```java
Folder createFromTemplate(String uuid, String dstPath,
boolean categories, boolean keywords, boolean notes,
Map properties)
Folder createFromTemplate(String uuid, String dstPath,
boolean categories, boolean keywords, boolean notes,
boolean propertyGroups, boolean security,
Map properties)
```
Property values must use ISO 8601 for dates and JSON array notation for multi-value fields:
```java
Map props = new HashMap<>();
props.put("okp:tpl.name", "sdk name");
props.put("okp:tpl.bird_date", ISO8601.formatBasic(Calendar.getInstance()));
props.put("okp:tpl.language", "[ \"java\" ]");
props.put("okp:technology.comment", "sdk comment");
Folder folder = ws.folder.createFromTemplate(
templateUuid, "/okm:root/sdk/template",
true, true, true, props);
```
---
## ws.quickImport — Quick Import
Optimised for bulk or high-performance scenarios. Executes fewer background steps than the standard `ws.document` and `ws.folder` creation methods.
### importDocument
```java
String importDocument(String uuid, File file)
```
Creates a document inside the folder or record identified by `uuid`. Returns the UUID of the created document.
```java
File file = new File("/tmp/logo.png");
String docUuid = ws.quickImport.importDocument(folderUuid, file);
System.out.println("Created: " + docUuid);
```
### importFolder
```java
String importFolder(String uuid, String name)
```
Creates a folder inside the parent folder or record identified by `uuid`. Returns the UUID of the created folder.
```java
String fldUuid = ws.quickImport.importFolder(parentUuid, "folder-name");
System.out.println("Created: " + fldUuid);
```
> Use `ws.quickImport` instead of `ws.document.create` / `ws.folder.create` when doing large batch imports where performance is critical.
---
---
## ws.mail — Mail
Mail nodes represent email messages stored in the repository. They can have attachments (as `Document` nodes), be imported from EML/MSG files, and be sent via the server's mail configuration.
### Core operations
```java
Mail getProperties(String uuid)
List getChildren(String uuid) // mails inside a folder/record
boolean isValid(String uuid)
String getPath(String uuid)
void delete(String uuid) // moves to trash
void purge(String uuid) // permanent deletion
Mail rename(String uuid, String newName)
void move(String uuid, String dstId)
Mail copy(String uuid, String dstId, String newName)
Mail extendedCopy(String uuid, String dstId,
boolean categories, boolean keywords,
boolean propertyGroups, boolean notes,
boolean security, String newName)
```
### Metadata
```java
void setTitle(String uuid, String title)
void setDescription(String uuid, String description)
void setNodeClass(String uuid, long nodeClass)
void setDispositionStage(String uuid, long stage)
String getExtractedText(String uuid)
```
### Attachments
```java
Document createAttachment(String uuid, String docName, InputStream is)
void deleteAttachment(String uuid, String docId)
List getAttachments(String uuid)
```
```java
// Add attachment
InputStream is = new FileInputStream("/tmp/logo.png");
ws.mail.createAttachment(mailUuid, "logo.png", is);
is.close();
// List attachments
List atts = ws.mail.getAttachments(mailUuid);
```
### Import (EML / MSG)
```java
Mail importEml(String uuid, String title, InputStream is)
Mail importMsg(String uuid, String title, InputStream is)
```
```java
InputStream is = new FileInputStream("/tmp/email.eml");
Mail mail = ws.mail.importEml(folderUuid, "Email Title", is);
is.close();
```
### Export / PDF
```java
InputStream getContent(String uuid) // raw EML stream
InputStream getThumbnail(String uuid, ThumbnailType type)
InputStream getPdf(String uuid)
Document saveAsPdf(String uuid, String newName, boolean propertyGroups, boolean security)
```
### Sending mail
```java
// Single recipient
void sendMail(String to, String subject, String body)
// Multiple recipients
void sendMail(List to, String subject, String body)
// With explicit sender
void sendMail(String from, List to, String subject, String body)
// With repository document attachments
Mail sendMailWithAttachments(String from, List to, List cc,
List bcc, List replyTo,
String subject, String body,
List docsId, String uuid)
```
```java
List to = Arrays.asList("user@example.com");
List docsId = Arrays.asList("46762f90-82c6-4886-8d21-ad3017dd78a7");
ws.mail.sendMailWithAttachments("sender@example.com", to,
new ArrayList<>(), new ArrayList<>(), new ArrayList<>(),
"Subject", "Body", docsId, folderUuid);
```
### Forward email
```java
void forwardEmail(String uuid, List users, List roles,
List mails, String message)
```
### Paginated listing
```java
SimpleNodeBaseResultSet getMailsPaginated(String context, int offset, int limit,
MailFilterQuery filter,
String orderColumn, boolean orderAsc)
```
`orderColumn` values: `uuid`, `subject`, `from`, `sentDate`, `hasAttachments`
```java
MailFilterQuery filter = new MailFilterQuery();
filter.setSubject("");
filter.setAttachments(AttachmentEnum.ALL);
SimpleNodeBaseResultSet results =
ws.mail.getMailsPaginated("/okm:root", 0, 20, filter, "subject", true);
```
### Wizard creation
```java
WizardNode createWizard(String uuid, String title, InputStream is, String type)
// type: Mail.ORIGIN_EML or Mail.ORIGIN_MSG
```
### Mail account management
```java
List getAccounts()
void addAccount(MailAccount account)
void updateAccount(MailAccount account)
void testAccount(MailAccount account)
void deleteAccount(long mailAccountId)
MailServerMessages getMailMessages(long accountId, long start)
void importMessages(long mailAccountId, List messageIds)
```
`MailAccount` protocol constants: `PROTOCOL_IMAPS`, `PROTOCOL_IMAP`, `PROTOCOL_POP3`, `PROTOCOL_POP3S`.
```java
MailAccount account = new MailAccount();
account.setMailProtocol(MailAccount.PROTOCOL_IMAPS);
account.setMailUser("inbox@gmail.com");
account.setMailPassword("secret");
account.setMailHost("imap.gmail.com");
account.setMailFolder("OpenKM");
account.setActive(true);
account.setRecursive(true);
ws.mail.addAccount(account);
```
### Mail filter management
```java
void createFilter(long mailAccountId, MailFilter filter)
void updateFilter(MailFilter filter)
void deleteFilter(long mailFilterId)
List getFilterRules(long filterId)
void createRule(long filterId, MailFilterRule rule)
void updateRule(MailFilterRule rule)
void deleteRule(long ruleId)
```
`MailFilterRule` field constants: `FIELD_FROM`, `FIELD_TO`, `FIELD_SUBJECT`, `FIELD_CONTENT`, `FIELD_ATTACHMENT`
`MailFilterRule` operation constants: `OPERATION_EQUALS`, `OPERATION_NOT_EQUALS`, `OPERATION_CONTAINS`, `OPERATION_ENDS_WITH`, `OPERATION_STARTS_WITH`
```java
MailFilterRule rule = new MailFilterRule();
rule.setField(MailFilterRule.FIELD_FROM);
rule.setOperation(MailFilterRule.OPERATION_CONTAINS);
rule.setValue("test@domain.com");
rule.setActive(true);
ws.mail.createRule(filterId, rule);
```
---
## ws.node — Generic Node Operations
Node operations work across all node types (documents, folders, mails, records). Access via `ws.node`.
### Node retrieval
```java
Node getNodeByUuid(String uuid)
List getNodesFiltered(List uuids)
```
### Version management
```java
List getVersionHistory(String uuid)
void restoreVersion(String uuid, String versionName)
void renameVersion(String uuid, String versionName, String newName)
void deleteVersion(String uuid, String versionName)
void purgeVersionHistory(String uuid)
void setComment(String uuid, String versionName, String comment)
```
### Locking
```java
LockInfo lock(String uuid)
LockInfo forceLock(String uuid) // requires ROLE_ADMIN
void unlock(String uuid)
void forceUnlock(String uuid) // requires ROLE_ADMIN
boolean isLocked(String uuid)
LockInfo getLockInfo(String uuid)
boolean hasNodesLockedByOtherUser(String uuid)
```
### Record promotion
```java
PromoteAsRecordEvaluation mayBePromotedAsRecord(String uuid, boolean fullEvaluation)
void promoteAsRecord(String uuid)
void degradeRecord(String uuid)
boolean isElectronicRecordPath(String uuid)
Record getElectronicRecordInPath(String uuid) // first Record ancestor in path
```
### Subscriptions
```java
void subscribe(String uuid)
void unsubscribe(String uuid)
```
### Paginated children
```java
SimpleNodeBaseList getChildrenNodesPaginated(
String uuid, int offset, int limit,
String filter, String orderByField, boolean orderAsc,
List filteredTypes)
SimpleNodeBaseList getChildrenNodesPaginated(
String uuid, int offset, int limit,
String filter, String orderByField, boolean orderAsc,
List filteredTypes, String pluginName)
SimpleNodeBaseList getChildrenNodesByCategoryPaginated(
String uuid, int offset, int limit,
String filter, String orderByField, boolean orderAsc,
List filteredTypes)
SimpleNodeBaseList getChildrenNodesByCategoryPaginated(
String uuid, int offset, int limit,
String filter, String orderByField, boolean orderAsc,
List filteredTypes, String pluginName)
```
`filteredTypes` uses constants from `SimpleNodeBase`: `TYPE_FOLDER`, `TYPE_DOCUMENT`, `TYPE_MAIL`, `TYPE_RECORD`
`orderByField` uses constants from `NodesPaginationInfo`: `ORDER_BY_NAME`, etc.
```java
List types = Arrays.asList(SimpleNodeBase.TYPE_FOLDER, SimpleNodeBase.TYPE_DOCUMENT);
SimpleNodeBaseList result = ws.node.getChildrenNodesPaginated(
parentUuid, 0, 10, "", NodesPaginationInfo.ORDER_BY_NAME, true, types);
// result.getNodes(), result.getTotal(), result.getFilteredCount()
```
### Navigation
```java
List getBreadcrumb(String uuid) // path from root to node
```
### ZIP import / export
```java
void importZip(String uuid, InputStream is)
void unZip(String uuid, String dstId)
InputStream exportZip(List uuids, boolean withPath, boolean background)
ZipDownloadEvaluationResult evaluateDownloadZip(List uuids)
```
```java
// Export nodes as ZIP
InputStream zip = ws.node.exportZip(Arrays.asList(uuid1, uuid2), true, false);
IOUtils.copy(zip, new FileOutputStream("/tmp/export.zip"));
```
### Download tokens
```java
String generateDownloadToken(String uuid, boolean preview, boolean oneTimeUse, Calendar validUntil)
```
- Download URL: `http://host/openkm/download?DTK=`
- Preview URL: `http://host/openkm/download?PTK=`
### Restore & misc
```java
Node restore(String uuid) // recovers a deleted node from trash
```
---
## ws.note — Notes
Notes are text annotations attached to any node (document, folder, mail, record). A note has its own UUID (returned in `note.getPath()`).
```java
Note add(String uuid, String text) // attach note to a node; returns Note
Note get(String noteId) // retrieve note by its UUID
void delete(String noteId) // remove note by its UUID
void set(String noteId, String text) // update note text
List list(String uuid) // all notes on a node
List getNoteHistories(String uuid) // full edit history of notes on a node
```
```java
// Add a note
Note note = ws.note.add(docUuid, "Reviewed and approved.");
String noteId = note.getPath(); // UUID used for subsequent operations
// List all notes on a node
for (Note n : ws.note.list(docUuid)) {
System.out.println(n.getText());
}
// Update note text
ws.note.set(noteId, "Reviewed, approved, and archived.");
// Delete note
ws.note.delete(noteId);
// Retrieve full history
for (NoteHistory nh : ws.note.getNoteHistories(docUuid)) {
System.out.println(nh);
}
```
---
---
## ws.notification — Notifications
Sends email notifications referencing repository nodes to OpenKM users, roles, and/or external email addresses.
```java
void notify(List uuids, List users, List roles,
List mails, String message, boolean attachment)
```
| Parameter | Description |
|-----------|-------------|
| `uuids` | UUIDs of nodes (documents, folders, mails, records) to reference |
| `users` | OpenKM usernames to notify |
| `roles` | OpenKM role names to notify |
| `mails` | External email addresses to notify |
| `message` | Email body text |
| `attachment` | `true` to attach the referenced node(s) to the email |
```java
List uuids = Arrays.asList("b153c4b7-3d1c-4589-bd42-0ed0f34fd338");
List users = Arrays.asList("test", "sochoa");
List roles = Arrays.asList("ROLE_TEST");
List mails = new ArrayList<>();
ws.notification.notify(uuids, users, roles, mails, "Body of the message", false);
```
---
## ws.pdf — PDF Operations
All operations work on documents stored in the repository that are PDF or convertible to PDF. Results are saved as new documents in the specified destination folder/record.
### getImage
```java
InputStream getImage(String uuid, int page, String size)
```
Converts a PDF page to an image. `page` starts at 1. `size` is the width in pixels (`null` defaults to 150).
```java
InputStream is = ws.pdf.getImage(docUuid, 1, null);
IOUtils.copy(is, new FileOutputStream("/tmp/page-1.png"));
is.close();
```
### split
```java
List split(String uuid, String dstId, String baseName, List pages)
```
Splits a PDF into multiple documents, one per specified page. Naming pattern: `baseName-001.pdf`, `baseName-003.pdf`, etc.
```java
List docs = ws.pdf.split(srcUuid, dstFolderUuid, "split", Arrays.asList(2, 3));
```
### extract
```java
boolean extract(String uuid, String dstId, String name, List pages)
```
Extracts specified pages into a new PDF document. Returns `true` on success.
```java
boolean ok = ws.pdf.extract(srcUuid, dstFolderUuid, "extract.pdf", Arrays.asList(2, 3));
```
### remove
```java
Document remove(String uuid, String dstId, String name, List pages)
```
Creates a new PDF with the specified pages removed. Returns the new `Document`.
```java
Document doc = ws.pdf.remove(srcUuid, dstFolderUuid, "without-pages.pdf", Arrays.asList(2, 3));
```
### rotate
```java
Document rotate(String uuid, String dstId, String name, Integer angle, List pages)
```
Rotates specified pages and saves as a new PDF. `angle` values: `90`, `180`, `270`.
```java
Document doc = ws.pdf.rotate(srcUuid, dstFolderUuid, "rotated.pdf", 180, Arrays.asList(2, 3));
```
### insertPages
```java
Document insertPages(String uuid, InsertPagesRequest request)
```
Merges pages from one or more source documents into a target PDF at specified positions. Returns the resulting `Document`.
```java
InsertPagesRequest request = new InsertPagesRequest();
request.name = "merged.pdf";
request.dstId = dstFolderUuid;
PageInsertOperation op = new PageInsertOperation();
op.srcId = sourceDocUuid;
op.pages = Arrays.asList(1, 2, 3);
op.insertFromPage = 1; // insert before page 1 of target
request.insertOperations.add(op);
Document result = ws.pdf.insertPages(targetDocUuid, request);
```
### optimize
```java
boolean optimize(String uuid)
```
Compresses and optimises a PDF in place. Returns `true` on success.
```java
boolean ok = ws.pdf.optimize(docUuid);
```
---
## ws.plugin — REST Plugins
Executes custom server-side REST plugins registered in OpenKM. Plugins must implement the `RestPlugin` interface and be annotated with `@PluginImplementation`. Identified by their fully qualified class name.
### executePluginPost
```java
Object executePluginPost(String className, Map parameters,
Class> clazz, InputStream is)
```
Invokes a plugin via POST. `clazz` is used to unmarshall the response. `is` can be `null` or a document stream to upload.
```java
Map params = new HashMap<>();
params.put("param1", "value1");
String result = (String) ws.plugin.executePluginPost(
"com.openkm.plugin.rest.TestRestPlugin", params, String.class, null);
```
### executePluginPostReturnFile
```java
InputStream executePluginPostReturnFile(String className,
Map parameters,
InputStream is)
```
Invokes a plugin via POST that returns a file stream.
```java
Map params = new HashMap<>();
params.put("docId", "/okm:root/invoices/invoice.pdf");
InputStream is = ws.plugin.executePluginPostReturnFile(
"com.openkm.plugin.rest.TestGetDocumentRestPlugin", params, null);
IOUtils.copy(is, new FileOutputStream("/tmp/invoice.pdf"));
is.close();
```
### executePluginGet
```java
Object executePluginGet(String className, Map parameters, Class> clazz)
```
Invokes a plugin via GET. Response is unmarshalled to `clazz`.
```java
Map params = new HashMap<>();
params.put("docId", "/okm:root/invoices/invoice.pdf");
String result = (String) ws.plugin.executePluginGet(
"com.openkm.plugin.rest.TestRestPlugin", params, String.class);
```
### executePluginGetReturnFile
```java
InputStream executePluginGetReturnFile(String className, Map parameters)
```
Invokes a plugin via GET that returns a file stream.
```java
Map params = new HashMap<>();
params.put("docId", "/okm:root/invoices/invoice.pdf");
InputStream is = ws.plugin.executePluginGetReturnFile(
"com.openkm.plugin.rest.TestGetDocumentRestPlugin", params);
IOUtils.copy(is, new FileOutputStream("/tmp/result.pdf"));
is.close();
```
---
---
## ws.property — Properties (Categories, Keywords, Encryption, Signatures)
Manages node-level metadata: categories, keywords, encryption flags, and digital signature status. Applies to documents, folders, mails, and records.
### Categories
```java
void addCategory(String uuid, String catId)
void removeCategory(String uuid, String catId)
```
`catId` is the UUID of the category folder node.
```java
ws.property.addCategory(docUuid, categoryFolderUuid);
ws.property.removeCategory(docUuid, categoryFolderUuid);
```
### Keywords
```java
void addKeyword(String uuid, String keyword)
void removeKeyword(String uuid, String keyword)
```
Keywords are case-sensitive. Use lowercase, single words or underscore-separated values (`"invoice"`, `"two_words"`).
```java
ws.property.addKeyword(docUuid, "invoice");
ws.property.addKeyword(docUuid, "pending_review");
ws.property.removeKeyword(docUuid, "pending_review");
```
### Encryption flag
```java
void setEncryption(String uuid, String cipherName)
void unsetEncryption(String uuid)
```
These methods mark the document as encrypted/unencrypted **without** performing actual cryptographic operations. They update the metadata flag only.
```java
ws.property.setEncryption(docUuid, "AES");
ws.property.unsetEncryption(docUuid);
```
### Digital signature flag
```java
void setSigned(String uuid, boolean signed)
boolean isSigned(String uuid)
```
Marks the document as signed or unsigned **without** performing cryptographic verification. Updates the metadata flag only.
```java
ws.property.setSigned(docUuid, true);
boolean signed = ws.property.isSigned(docUuid);
```
---
---
## ws.propertyGroup — Metadata Groups (Property Groups)
Metadata groups are collections of structured fields (inputs, selects, dates, checkboxes, textareas, suggestboxes) attached to nodes. Group and field names follow the JCR namespace convention: `okg:groupName` for groups and `okp:groupName.fieldName` for fields.
**Date handling** — use `com.openkm.sdk4j.util.ISO8601`:
```java
// Write
properties.put("okp:technology.date", ISO8601.formatBasic(Calendar.getInstance()));
// Read back
Calendar cal = ISO8601.parseBasic(properties.get("okp:technology.date"));
```
**Multi-value select fields** — use JSON array strings:
```java
properties.put("okp:technology.type", "[ \"t1\", \"t2\" ]");
// Or with Gson:
properties.put("okp:technology.language", new Gson().toJson(Arrays.asList("java", "python")));
```
### Group assignment
```java
void addGroup(String uuid, String grpName, Map propertiesMap)
void removeGroup(String uuid, String grpName)
boolean hasGroup(String uuid, String grpName)
```
> `addGroup` must be called before `setProperties`. Partial property maps are accepted — omitted fields retain their default/existing values.
```java
Map props = new HashMap<>();
props.put("okp:technology.comment", "initial comment");
props.put("okp:technology.date", ISO8601.formatBasic(Calendar.getInstance()));
props.put("okp:technology.type", "[ \"t1\", \"t2\" ]");
ws.propertyGroup.addGroup(uuid, "okg:technology", props);
boolean has = ws.propertyGroup.hasGroup(uuid, "okg:technology");
ws.propertyGroup.removeGroup(uuid, "okg:technology");
```
### Reading group data
```java
List getGroups(String uuid) // groups assigned to a node
List getAllGroups() // all groups in the system
List getAllGroups(String uuid) // all groups, filtered by automation events for node
PropertyGroup getGroup(String grpName) // group definition by name
Map getProperties(String uuid, String grpName) // field values as key→value map
```
```java
Map values = ws.propertyGroup.getProperties(uuid, "okg:technology");
for (Map.Entry e : values.entrySet()) {
System.out.println(e.getKey() + " > " + e.getValue());
}
```
### Updating field values
```java
void setProperties(String uuid, String grpName, Map properties)
```
Partial updates allowed — only specified keys are modified.
### Form element definitions
Form elements describe the field schema (type, label, options, validators, etc.).
```java
List getPropertyGroupFormDefinition(String grpName) // empty form definition
List getPropertyGroupFormDefinition(String uuid, String grpName) // definition in node context
List getPropertyGroupForm(String uuid, String grpName) // definition + current values
```
### Version-specific access
```java
List getGroupsByVersion(String uuid, String versionName)
Map getPropertiesByVersion(String uuid, String grpName, String versionName)
List getPropertiesByVersionForm(String uuid, String grpName, String versionName)
```
```java
Map v13 = ws.propertyGroup.getPropertiesByVersion(uuid, "okg:technology", "1.3");
```
### Suggestbox helpers
```java
List getSuggestions(String uuid, String grpName, String propName)
String getSuggestBoxKeyValue(String grpName, String propertyName, String key)
Map getSuggestBoxKeyValuesFiltered(String grpName, String propertyName, String filter)
```
```java
// Autocomplete suggestions
List opts = ws.propertyGroup.getSuggestions(uuid, "okg:technology", "okp:technology.language");
// Lookup a single key
String val = ws.propertyGroup.getSuggestBoxKeyValue("okg:consulting", "okp:consulting.suggestbox", "countryKey");
// Filter key-value pairs
Map filtered = ws.propertyGroup.getSuggestBoxKeyValuesFiltered(
"okg:consulting", "okp:consulting.suggestbox", "pai");
```
### Field validation
```java
String validateField(String value, String className, List uuids)
```
Invokes a custom `FieldValidator` plugin (identified by fully qualified class name). Returns an empty string if valid, or an error message.
```java
String msg = ws.propertyGroup.validateField(
"test",
"com.openkm.plugin.form.validator.DuplicateDocumentNumberValidator",
Arrays.asList(uuid1, uuid2));
if (!msg.isEmpty()) System.out.println("Validation error: " + msg);
```
### Definition management (ROLE_ADMIN only)
```java
String getRegisteredDefinition() // returns XML definition
void registerDefinition(InputStream is, String name) // applies XML definition
```
```java
// Read current definition
System.out.println(ws.propertyGroup.getRegisteredDefinition());
// Upload new definition
InputStream is = new FileInputStream("/tmp/PropertyGroups.xml");
ws.propertyGroup.registerDefinition(is, "test");
is.close();
```
**Minimal XML definition structure:**
```xml
```
---
## ws.record — Records
Records are a special node type for formal records management. They behave similarly to folders (they can contain documents, mails, and child records) but carry additional classification, disposition, and lifecycle metadata.
### Creation
```java
Record create(String uuid, String name, String title, long nodeClass)
WizardNode createWizard(String uuid, String name, String title, long nodeClass)
void createMissingRecords(String recPath)
```
- `uuid` — parent folder or record UUID
- `nodeClass` — business type (series) ID from the file plan
- `createMissingRecords` creates all missing intermediate records in the given path
```java
Record rec = ws.record.create(parentUuid, "Q1-2024", "Q1 2024 Invoices", nodeClassId);
```
### Reading
```java
Record getProperties(String uuid)
List getChildren(String uuid)
boolean isValid(String uuid)
String getPath(String uuid)
```
### Rename, move, copy, delete
```java
Record rename(String uuid, String newName)
void move(String uuid, String dstId)
Record copy(String uuid, String dstId, String newName)
Record extendedCopy(String uuid, String dstId, String newName,
boolean categories, boolean keywords,
boolean propertyGroups, boolean notes, boolean security)
void delete(String uuid) // moves to trash
void purge(String uuid) // permanent — no recovery without backup
```
### Metadata
```java
void setTitle(String uuid, String title)
void setDescription(String uuid, String description)
void setNodeClass(String uuid, long nodeClass)
void setDispositionStage(String uuid, long stage)
```
### Templates
```java
Record createFromTemplate(String uuid, String dstPath,
boolean categories, boolean keywords, boolean notes,
Map properties)
Record createFromTemplate(String uuid, String dstPath,
boolean categories, boolean keywords, boolean notes,
boolean propertyGroups, boolean security,
Map properties)
```
Property map values must use ISO 8601 for dates and JSON array notation for multi-value fields.
---
## ws.relation — Node Relations
Relations link pairs of nodes. Three relation types are supported: `RelationType.BIDIRECTIONAL`, `RelationType.PARENT_CHILD`, `RelationType.MANY_TO_MANY`. MANY_TO_MANY relations can be further organised into named groups.
### Relation types
```java
List getRelationTypes(String type)
// type: RelationType.BIDIRECTIONAL | RelationType.PARENT_CHILD | RelationType.MANY_TO_MANY
```
### Simple relations
```java
void add(String nodeAId, String nodeBId, long relTypeId)
void delete(long relationId)
List getRelations(String uuid)
```
```java
ws.relation.add(docUuidA, docUuidB, relTypeId);
for (Relation r : ws.relation.getRelations(docUuidA)) {
System.out.println(r);
}
```
### Relation groups (MANY_TO_MANY)
```java
void addGroup(String uuid, String groupName, long type)
void addNodeToGroup(String uuid, long groupId)
void deleteItemFromGroup(String uuid, long groupId)
void deleteGroup(long groupId)
RelationGroup getGroup(long groupId)
void setGroupName(long groupId, String groupName)
List getRelationGroups(String uuid)
RelationGroupResultSet getAllRelationGroups(long relationTypeId, String filter,
int offset, int limit)
```
```java
// Create a group and add nodes
ws.relation.addGroup(anchorUuid, "Invoice Set 2024", relTypeId);
RelationGroup group = ws.relation.getRelationGroups(anchorUuid).get(0);
ws.relation.addNodeToGroup(otherUuid, group.getId());
// Paginated listing of all groups of a type
RelationGroupResultSet result =
ws.relation.getAllRelationGroups(relTypeId, "", 0, 20);
```
---
---
## ws.report — Reports
Reports are pre-configured queries that can be executed and exported in multiple formats.
**Format constants** (defined in `Report`):
`FORMAT_CSV`, `FORMAT_DOCX`, `FORMAT_HTML`, `FORMAT_ODT`, `FORMAT_PDF`, `FORMAT_RTF`, `FORMAT_TEXT`
**Date parameters** — use `yyyy-MM-dd` format (e.g. `"2024-01-01"`).
**Multi-value parameters** — separate with comma (e.g. `"value1,value2"`).
### JasperReports-based reports
```java
List getReports(boolean active)
Report getReport(long rpId)
String generateDownloadReportToken(long rpId)
InputStream execute(long rpId, Map params, String format, String uuid)
Document save(long rpId, Map params, String format,
String dstId, String docName, String uuid)
```
- `uuid` — optional context node UUID; pass `""` when not needed
- `execute` returns the report as a stream; `save` stores it as a `Document` in the repository
```java
Map params = new HashMap<>();
params.put("from_date", "2024-01-01");
params.put("to_date", "2024-06-30");
// Stream to local file
InputStream is = ws.report.execute(1L, params, Report.FORMAT_PDF, "");
IOUtils.copy(is, new FileOutputStream("/tmp/report.pdf"));
is.close();
// Save directly to repository
Document doc = ws.report.save(1L, params, Report.FORMAT_PDF,
dstFolderUuid, "report.pdf", "");
```
### SQL-based reports
```java
List getSqlReports(boolean active)
InputStream executeSql(long rpId)
Document saveSql(long rpId, Map params, String dstId, String docName)
```
```java
List reports = ws.report.getSqlReports(true);
InputStream is = ws.report.executeSql(1L);
IOUtils.copy(is, new FileOutputStream("/tmp/activity.csv"));
is.close();
Document doc = ws.report.saveSql(1L, new HashMap<>(), dstFolderUuid, "activity.csv");
```
---
## ws.repository — Repository
Provides access to well-known repository paths, node lookup utilities, scripting, raw SQL/HQL queries, translations, and system information.
### Well-known folders
```java
Folder getRootFolder() // /okm:root
Folder getTrashFolder() // /okm:trash/{userId}
Folder getTrashFolderBase() // /okm:trash
Folder getTemplatesFolder() // /okm:templates
Folder getPersonalFolder() // /okm:personal/{userId}
Folder getPersonalFolderBase() // /okm:personal
Folder getMailFolder() // /okm:mail/{userId}
Folder getMailFolderBase() // /okm:mail
Folder getCategoriesFolder() // /okm:categories
```
### Trash
```java
void purgeTrash() // permanently removes all nodes from the user's trash
```
> Purged nodes can only be restored from a repository backup.
### Node lookup & path conversion
```java
boolean hasNode(String nodeId) // nodeId can be UUID or path
String getNodePath(String uuid)
String getNodeUuid(String nodePath)
```
### System info
```java
AppVersion getAppVersion()
String getRepositoryUuid()
String getClusterUuid()
String getUpdateMessage()
String getServerTime() // ISO8601 format
LicenseInfo getLicenseInfo()
```
### Attribute copy
```java
void copyAttributes(String uuid, String dstId,
boolean categories, boolean keywords,
boolean propertyGroups, boolean notes)
```
### Scripting — ROLE_ADMIN only
Executes server-side BeanShell scripts.
```java
ScriptExecutionResult executeScript(InputStream is)
ScriptExecutionResult executeScript(String script)
```
`ScriptExecutionResult` provides `getResult()`, `getStdout()`, `getStderr()`.
```java
InputStream is = new FileInputStream("/tmp/script.bsh");
ScriptExecutionResult r = ws.repository.executeScript(is);
System.out.println(r.getResult());
if (!r.getStderr().isEmpty()) System.out.println("Error: " + r.getStderr());
is.close();
```
### SQL queries — ROLE_ADMIN only (single statement)
```java
SqlQueryResults executeSqlQuery(InputStream is)
SqlQueryResults executeSqlQuery(String sql)
```
```java
SqlQueryResults result = ws.repository.executeSqlQuery(
"SELECT NBS_UUID, NBS_NAME FROM OKM_NODE_BASE LIMIT 10;");
for (SqlQueryResultColumns row : result.getResults()) {
System.out.println(row.getColumns().get(0) + " / " + row.getColumns().get(1));
}
```
### HQL queries — ROLE_ADMIN only (single statement)
```java
HqlQueryResults executeHqlQuery(InputStream is)
HqlQueryResults executeHqlQuery(String hql)
```
```java
HqlQueryResults result = ws.repository.executeHqlQuery(
"SELECT uuid, author FROM NodeBase WHERE name = 'okm:root';");
for (HqlQueryResultColumns row : result.getResults()) {
System.out.println(row.getColumns().get(0));
}
```
### Translations & configuration
```java
Map getTranslations(String lang, String module)
// module: "frontend" | "extension" | "mobile" | custom module name
Configuration getConfiguration(String key)
// note: may be restricted to ROLE_ADMIN depending on 'webservices.visible.properties' setting
```
### Change log & locales
```java
List getChangeLog(String nodePath, Calendar modificationsFrom)
Map getAvailableLocales(String locale)
```
---
## ws.search — Search
The search service supports structured queries via `QueryParams`, Lucene query strings, and paginated result sets.
### QueryParams fields
| Field | Description |
|-------|-------------|
| `domain` | Node types to include: `QueryParams.DOCUMENT`, `FOLDER`, `MAIL`, `RECORD` — combine with `\|` |
| `name` | Node name filter; supports wildcards (`*`, `?`) |
| `content` | Full-text content filter; supports wildcards |
| `author` | Creator userId filter |
| `title` | Title filter; supports wildcards |
| `keywords` | Set of keyword strings |
| `categories` | Set of category folder UUIDs |
| `mimeType` | MIME type filter (documents only) |
| `language` | Language code filter (documents only) |
| `folder` | Parent folder UUID (defaults to `/okm:root`) |
| `folderRecursive` | `true` to search recursively |
| `lastModifiedFrom` / `lastModifiedTo` | Calendar date range |
| `mailSubject`, `mailFrom`, `mailTo` | Mail-specific filters; support wildcards |
| `notes` | Notes text filter; supports wildcards |
| `properties` | `Map` for metadata field filtering |
| `queryName` | Required when saving a search |
**Domain combination:** `params.setDomain(QueryParams.DOCUMENT | QueryParams.FOLDER)`
**Date range in metadata:** `"ISO8601.formatBasic(from),ISO8601.formatBasic(to)"`
**Multiple metadata values:** `"value1;value2"`
**All search conditions are combined with AND logic.**
### Sort field constants (`SearchSortField`)
`NAME`, `AUTHOR`, `LAST_MODIFIED`
### find — full objects
```java
List find(QueryParams queryParams, String propertiesPlugin)
List find(QueryParams queryParams, String sortField, boolean sortReverse,
String propertiesPlugin)
```
`propertiesPlugin` — canonical class name implementing `NodeProperties`; pass `null` for default behaviour.
```java
QueryParams params = new QueryParams();
params.setDomain(QueryParams.DOCUMENT | QueryParams.FOLDER);
params.setName("invoice*");
for (QueryResult qr : ws.search.find(params, null)) {
System.out.println(qr);
}
```
### findSimpleNodeBasePaginated — lightweight paginated
Returns `SimpleNodeBase` objects (~60-70% faster than full nodes). Preferred for large result sets.
```java
SimpleNodeBaseResultSet findSimpleNodeBasePaginated(QueryParams queryParams, int offset, int limit)
SimpleNodeBaseResultSet findSimpleNodeBasePaginated(QueryParams queryParams,
String sortField, boolean sortReverse, int offset, int limit)
SimpleNodeBaseResultSet findSimpleNodeBasePaginated(QueryParams queryParams,
String sortField, boolean sortReverse, int offset, int limit,
String pluginName)
```
```java
SimpleNodeBaseResultSet rs = ws.search.findSimpleNodeBasePaginated(params, 0, 20);
System.out.println("Total: " + rs.getTotal());
for (SimpleNodeBase sn : rs.getResults()) {
System.out.println(sn);
}
```
### findWithMetadata — results with metadata group values
```java
List findWithMetadata(QueryParams params, String propertiesPlugin, List groups)
List findWithMetadata(QueryParams params, String sortField, boolean sortReverse,
String propertiesPlugin, List groups)
ResultSet findWithMetadataPaginated(QueryParams queryParams, int offset, int limit,
String propertiesPlugin, List groups)
ResultSet findWithMetadataPaginated(QueryParams queryParams, String sortField, boolean sortReverse,
int offset, int limit, String propertiesPlugin, List groups)
```
```java
List groups = Arrays.asList("okg:consulting");
ResultSet rs = ws.search.findWithMetadataPaginated(params, 0, 10, null, groups);
System.out.println("Total: " + rs.getTotal());
```
### findByQuery — Lucene query syntax
```java
List findByQuery(String query, String propertiesPlugin)
List findByQuery(String query, String sortField, boolean sortReverse,
String propertiesPlugin)
SimpleNodeBaseResultSet findSimpleNodeBaseByQueryPaginated(String query, int offset, int limit)
SimpleNodeBaseResultSet findSimpleNodeBaseByQueryPaginated(String query,
String sortField, boolean sortReverse, int offset, int limit)
```
Lucene field syntax: `field:value`. Combine with `AND`, `OR`. Wildcards supported.
```java
List results = ws.search.findByQuery("keyword:test AND name:invoice*.pdf", null);
SimpleNodeBaseResultSet rs = ws.search.findSimpleNodeBaseByQueryPaginated(
"text:contract AND name:*.pdf", SearchSortField.LAST_MODIFIED, true, 0, 10);
```
### Category search
```java
List getCategorizedDocuments(String categoryId) // categoryId = category folder UUID
```
### Saved searches
```java
long saveSearch(QueryParams params) // params must have queryName set
void updateSearch(QueryParams params) // only creator can update
QueryParams getSearch(int qpId) // only creator can access
List getAllSearches() // all searches by current user
void deleteSearch(int qpId) // only creator can delete
List findAllDefaultByNodeClass(long ncId)
```
### Other
```java
NodeSearchConfig getSearchConfig(String pluginName)
InputStream csvExport(String lang, QueryParams queryParams,
List propertyGroups, boolean compact)
// lang: ISO 639-1 code (e.g. "es-ES"); compact=true omits empty columns
```
```java
InputStream is = ws.search.csvExport("en-GB", params, new ArrayList<>(), false);
IOUtils.copy(is, new FileOutputStream("/tmp/results.csv"));
is.close();
```
---
---
## ws.shard — Shards
Shards partition repository binary storage across multiple locations. Three methods are available.
```java
List getShards()
void setCurrentShard(long shardId) // change active shard for the session
void changeShard(String uuid, long shardId) // reassign a node's binary to a different shard
```
```java
for (Shard shard : ws.shard.getShards()) {
System.out.println(shard);
}
ws.shard.setCurrentShard(2L);
ws.shard.changeShard("e2391b01-ce10-42c7-94a9-b0d6b394048e", 2L);
```
---
## ws.stamp — Stamps
Stamps apply text, images, or barcodes onto PDF documents. They can use predefined stamp configurations or be applied with custom parameters and expressions.
### Positioning expressions
The stamping engine resolves macro expressions to pixel coordinates:
`IMAGE_WIDTH`, `IMAGE_HEIGHT`, `PAGE_WIDTH`, `PAGE_HEIGHT`, `PAGE_CENTER`, `PAGE_MIDDLE`
Example — horizontal centering: `"PAGE_CENTER - IMAGE_WIDTH / 2"`
### Discovery
```java
List getAllStamps()
StampText getStampTextByPk(long id, String uuid)
StampImage getStampImageByPk(long id, String uuid)
StampBarcode getStampBarcodeByPk(long id, String uuid)
```
### Apply predefined stamps
```java
void stampText(String uuid, long id)
void stampImage(String uuid, long id)
void stampBarcode(String uuid, long id)
```
```java
ws.stamp.stampText(docUuid, stampTextId);
ws.stamp.stampImage(docUuid, stampImageId);
ws.stamp.stampBarcode(docUuid, stampBarcodeId);
```
### Apply stamps with custom parameters
```java
void stampTextCustom(String uuid, long stampTextId, String text,
String exprX, String exprY, String range)
void stampImageManually(String uuid, long id, String exprX, String exprY,
String range, String personalStampUuid)
void stampImageCustom(String uuid, long stampImageId, String exprX, String exprY,
String range, double ratio, InputStream is)
```
- `exprX` / `exprY` — positioning expressions (see macros above)
- `range` — page range string (e.g. `"1-3"`, `"all"`)
- `ratio` — scale factor for custom image
- `is` — image stream for `stampImageCustom`
### Coordinate helpers
```java
StampCoordinates calculateStampCoordinates(
long imgToStampWidth, long imgToStampHeight,
long floatingDivWidth, long floatingDivHeight,
String exprX, String exprY,
String stampType, String stampAlign)
StampExpressions calculateStampExpressions(
long imgToStampWidth, long imgToStampHeight,
long posX, long posY)
StampImageSize calculateFlyImageDimensions(
String uuid,
long imgToStampWidth, long imgToStampHeight,
long floatingImageWidth, long floatingImageHeight,
int pageNumber)
```
### Personal stamps
```java
List getPersonalStamps() // user's seal images under /okm:templates/{userId}/stamps
StampPersonalImage getPersonalStampImage(String uuid, String imageUuid, long id)
```
---
## ws.task — Task Management
Full task management including tasks, projects, types, statuses, notes, and history.
### Task fields reference
| Field | Type | Notes |
|-------|------|-------|
| `subject` | String | Required |
| `start` / `end` | String (ISO8601) | `start` required |
| `description` | String | Optional |
| `statusId` | long | Required |
| `projectId` | long | Required |
| `typeId` | long | Required |
| `user` | String | Assigned OpenKM userId |
| `progress` | int | 0–100 (only on update) |
| `repeatExpression` | String | Cron syntax: `"min hour day month dayOfWeek"` |
| `repeatUntil` | String | ISO8601 end date for recurrence |
| `repeatTimes` | int | Max repetitions |
| `reminderStartUnit` / `reminderEndUnit` | String | `"m"` = minutes, `"h"` = hours, `"d"` = days |
| `reminderStartValue` / `reminderEndValue` | int | Quantity before start/end |
**Repeat expression examples:**
- `"0 0 1 * *"` — midnight on the 1st of every month
- `"0 9 * * 1"` — every Monday at 09:00
### Create / update / get / delete
```java
Task create(String subject, String start, String end, String description,
long statusId, long projectId, long typeId, String user,
List notificationUsers, List externalUsers,
List relatedDocuments, List relatedFolders,
List relatedRecords, List relatedMails,
String repeatExpression, String repeatUntil, String formatDate,
int repeatTimes, String reminderStartUnit, int reminderStartValue,
String reminderEndUnit, int reminderEndValue)
Task update(long taskId, String subject, String start, String end, String description,
long statusId, long projectId, long typeId, String user,
List notificationUsers, List externalUsers,
List relatedDocuments, List relatedFolders,
List relatedRecords, List relatedMails, String owner,
String repeatExpression, String repeatUntil, String formatDate,
int repeatTimes, int progress,
String reminderStartUnit, int reminderStartValue,
String reminderEndUnit, int reminderEndValue)
Task getTask(long taskId)
void delete(long taskId)
```
```java
String start = ISO8601.formatBasic(Calendar.getInstance());
Calendar endCal = Calendar.getInstance();
endCal.add(Calendar.DATE, 15);
String end = ISO8601.formatBasic(endCal);
Task task = ws.task.create(
"Review contract", start, end, "Review and sign",
statusId, projectId, typeId, "okmAdmin",
Arrays.asList("sochoa"), new ArrayList<>(),
Arrays.asList(docUuid), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(),
"", "", "dd-MM-yyyy", 0, "d", 1, "d", 1);
```
### Listing tasks (all methods have two overloads: with and without date range)
```java
// Without date range
TaskList getAssigned(long projectId, long typeId, long statusId,
String orderColumn, boolean orderAsc, int offset, int limit,
String subject)
// With date range
TaskList getAssigned(long projectId, long typeId, long statusId,
String orderColumn, boolean orderAsc, int offset, int limit,
Calendar from, Calendar to, String subject)
// Same overloads exist for:
TaskList getActive(...)
TaskList getFinished(...)
TaskList getNotified(...)
```
`orderColumn` values: `"subject"`, `"start"`, `"end"`, `"progress"`, `"owner"`
```java
TaskList list = ws.task.getAssigned(
projectId, typeId, statusId, "subject", true, 0, 10,
from, to, "");
for (Task t : list.getTasks()) {
System.out.println(t);
}
```
### Count methods
```java
long getAssignedCount(long statusId, long projectId, long typeId)
long getActiveCount(long statusId, long projectId, long typeId)
long getFinishedCount(long statusId, long projectId, long typeId)
long getNotifiedCount(long statusId, long projectId, long typeId)
```
### Related tasks & history
```java
TaskList getRelatedTasks(String uuid) // tasks related to a node UUID
List getHistory(long taskId)
```
### Projects
```java
List getProjects(boolean filterActive)
TaskProject getProject(long projectId)
TaskProject createProject(String name, boolean active, String description)
TaskProject updateProject(long projectId, boolean active, String name, String description)
void deleteProject(long projectId)
```
### Types
```java
List getTypes(boolean filterActive)
TaskType getType(long typeId)
TaskType createType(String name, boolean active, String description)
TaskType updateType(long typeId, boolean active, String name, String description)
void deleteType(long typeId)
```
### Statuses
```java
List getStatus()
TaskStatus getStatus(long statusId)
TaskStatus createStatus(String name, boolean finish) // finish=true marks as completion status
TaskStatus updateStatus(long statusId, String name, boolean finish)
void deleteStatus(long statusId)
```
### Notes
```java
List getNotes(long taskId)
TaskNote createNote(long taskId, String text)
TaskNote updateNote(long noteId, String text)
void deleteNote(long noteId)
```
---
---
## ws.userConfig — User Configuration
Two methods for reading and writing the current user's home node setting.
```java
UserConfig getConfig()
void setHome(String uuid)
```
```java
UserConfig cfg = ws.userConfig.getConfig();
System.out.println("User: " + cfg.getUser());
Node home = ws.node.getNodeByUuid(cfg.getHomeNode());
System.out.println("Home: " + home.getPath());
// Change home node
ws.userConfig.setHome("53751cb7-c462-4320-ba46-4cc33e4667ae");
```
---
## ws.wizard — Wizard
Wizards are pending action queues attached to nodes after creation via `createWizard`. They track what the user still needs to do: assign categories, keywords, metadata groups, OCR capture, digital signatures, or start a workflow. All `add*` methods accept an `order` parameter to control step sequence.
> **Important:** `setAutostart` must always be set **last** when configuring a wizard.
### Querying wizards
```java
List findByUser() // pending wizards for current user
List findAllByUser() // all wizards (including completed) for current user
WizardNode findByUserAndUuid(String uuid) // wizard for a specific node
boolean hasWizardByUserAndNode(String uuid)
```
### Deleting / postponing
```java
void deleteAll() // remove all pending wizards for current user
void deleteByNode(String uuid) // remove wizard for a specific node
void postponeNode(String uuid, Calendar date) // postpone wizard until given date
void cancelPostponedNode(String uuid) // cancel a postponement
```
### Adding / removing wizard steps (all `add*` require `int order`)
```java
void addDigitalSignature(String uuid, int order)
void removeDigitalSignature(String uuid)
void addShowWizardCategories(String uuid, int order)
void removeShowWizardCategories(String uuid)
void addShowWizardKeywords(String uuid, int order)
void removeShowWizardKeywords(String uuid)
void addShowWizardOCRDataCapture(String uuid, int order)
void removeShowWizardOCRDataCapture(String uuid)
void addGroup(String uuid, String group, int order) // metadata group name e.g. "okg:tpl"
void removeGroup(String uuid, String group)
void addWorkflow(String uuid, String workflow, int order) // workflow name e.g. "purchase"
void removeWorkflow(String uuid, String workflow)
void setAutostart(String uuid) // must be configured last
```
```java
ws.wizard.addShowWizardCategories(uuid, 1);
ws.wizard.addShowWizardKeywords(uuid, 2);
ws.wizard.addGroup(uuid, "okg:tpl", 3);
ws.wizard.addWorkflow(uuid, "purchase", 4);
ws.wizard.setAutostart(uuid); // always last
```
---
## ws.workflow — Workflow
Manages jBPM-based workflow process definitions and instances.
### Process definitions
```java
void registerProcessDefinition(InputStream is) // upload a .par file
void deleteProcessDefinition(long pdId)
ProcessDefinition getProcessDefinition(long pdId)
List findAllProcessDefinitions()
List findLatestProcessDefinitions() // latest version per name
ProcessDefinition findLastProcessDefinition(String name)
String getProcessDefinitionImage(long pdId, String uuid) // Base64 diagram image
Map> getProcessDefinitionForms(long pdId)
```
### Running processes
```java
// Start workflow associated with a node
ProcessInstance runProcessDefinition(long pdId, String uuid, Map propertiesMap)
// Start workflow without node association
ProcessInstance runProcessDefinition(long pdId, Map propertiesMap)
ProcessInstance getProcessInstance(long piId)
List findProcessInstances(long pdId)
List findProcessInstancesByNode(String uuid)
```
### Task instances
```java
TaskInstanceResultSet findUserTaskInstances(int offset, int limit) // tasks assigned to current user
TaskInstanceResultSet findPooledTaskInstances(int offset, int limit) // unassigned tasks available to current user
List findTaskInstances(long piId) // all tasks for a process instance
TaskInstance getTaskInstance(long tiId)
void startTaskInstance(long tiId)
void setTaskInstanceActorId(long tiId, String actorId) // assign to user
void endTaskInstance(long tiId, String transName) // e.g. "end", "approve", "reject"
void setTaskInstanceValues(long pdId, long tiId, String tiName,
String transName, Map properties)
```
```java
// List tasks for current user
TaskInstanceResultSet tasks = ws.workflow.findUserTaskInstances(0, 20);
// Complete a task
ws.workflow.startTaskInstance(tiId);
Map values = new HashMap<>();
values.put("field1", "value1");
ws.workflow.setTaskInstanceValues(pdId, tiId, "Review", "approve", values);
ws.workflow.endTaskInstance(tiId, "approve");
```
### Search / overview (paginated)
```java
ProcessInstanceResultSet searchAssignedTasks(Calendar start, Calendar end,
String status, Long procDefId, int offset, int limit)
ProcessInstanceResultSet searchStartedByMe(Calendar start, Calendar end,
String status, Long procDefId, int offset, int limit)
ProcessInstanceResultSet searchOverview(Calendar start, Calendar end,
String status, Long procDefId, String initiator, String taskUser,
int offset, int limit)
```
### SuggestBox helpers (workflow forms)
```java
String getSuggestBoxKeyValue(long pdId, String uuid, String taskName,
String propertyName, String key)
Map getSuggestBoxKeyValuesFiltered(long pdId, String uuid, String taskName,
String propertyName, String filter)
```
---