Skip to main content

Part 2 — FormManager (FM)

Table of Contents


FM Proxy Table

In addition to its own direct endpoints, FM acts as a transparent proxy for all workflow-related requests. The following WFM endpoints are accessed through FM — it forwards the requests to WFM, adding correlation headers and enforcing timeouts. Default proxy timeout is 60 seconds unless noted otherwise.

FM Proxy PathWFM TargetMethodTimeout
/context/contextPOST60s
/context/activity/context/activityPOST60s
/action/complete/action/completePOST60s
/action/save/action/savePOST60s
/action/retry/action/retryPOST60s
/action/function/action/functionPOST60s
/action/start/process/startPOST60s
/process/start/process/startPOST60s
/process/:processInstanceId/process/:processInstanceIdGET60s
/task/:taskId/run/:functionId/task/:taskId/run/:functionIdPOST60s
/task/:taskId/commit/task/:taskId/commitPOST60s
/note/add/note/addPOST60s
/note/update/note/updatePOST60s
/note/delete/note/deletePOST60s
/file/upload/file/uploadPOST300s
/file/download/file/downloadPOST300s
/file/delete/file/deletePOST300s
/notifications/:appId/notifications/:appIdGET60s
/notify/send/notify/sendPOST60s
/query/activeProcessIds/query/activeProcessIdsPOST60s
/query/processIdsUpdatedBetween/query/processIdsUpdatedBetweenPOST60s
/query/processInstanceContextAndActivity/query/processInstanceContextAndActivityPOST60s
/getConstantValue/getConstantValuePOST60s
/appclose/appclosePOST60s
/external/task/:taskId/action/:actionType/external/task/:taskId/action/:actionTypePOST60s
/external/task/:taskId/assignToUser/external/task/:taskId/assignToUserPOST60s
/external/task/:taskId/assignToSwimlane/external/task/:taskId/assignToSwimlanePOST60s
/external/function/:functionId/execute/external/function/:functionId/executePOST60s
/external/process/start/external/process/startPOST60s
/external/process/changeStep/external/process/changeStepPOST60s
/external/data/search/:collection/external/data/search/:collectionPOST60s
/external/locks/:userId/external/locks/:userIdDELETE60s
/external/batchjob/retry/external/batchjob/retryPOST60s

For details on each of these WFM endpoints, see Part 3 — WorkflowManager.


FM Direct Endpoints

The following endpoints are served directly by FormManager and do not proxy to WorkflowManager.


User Management

POST /user/createUser

Creates a new workflow user in the system. The server generates a unique userId via UUID and stores the user record. A case-insensitive duplicate check is performed on fullName to ensure uniqueness across all users in the system.

Request Body:

{
firstName: string;
lastName: string;
fullName: string; // Must be unique (case-insensitive)
unit: { id: string; name: string };
roles: string[]; // At least one role required
email?: string;
swimlanes?: string[]; // Swimlane assignments for task routing
isSystemUser?: boolean; // Defaults to false
organizationId: string; // Auto-injected
appId: string; // Auto-injected
}
FieldRequiredDescription
fullNameYesDisplay name for the user. Must be unique across all users (case-insensitive check).
unitYesThe organizational unit the user belongs to, containing an id and a name.
rolesYesArray of role identifiers assigned to the user. At least one role must be provided.
emailNoUser's email address.
swimlanesNoList of swimlane names this user can participate in. Used for task routing — when a task is assigned to a swimlane, only users with that swimlane can pick it up.
isSystemUserNoWhether this is a system/service account rather than a human user.

Response: IUser — the created user object including the server-generated userId.

Errors:

  • 400: fullName is missing → "Name is mandatory!"
  • 400: unit is missing → "Unit is mandatory!"
  • 400: roles is empty → "User role is mandatory!"
  • 400: Duplicate fullName"Username is already taken."

POST /user/updateUser

Updates an existing workflow user. The user is located by userId and the entire document is replaced with the provided fields. Unlike createUser, this endpoint does not perform uniqueness checks on fullName or validate required fields — it trusts the caller to provide a complete, valid user object.

Request Body: Same structure as createUser. The userId field is used as the lookup key.

Response: IUser — the updated user object.

Errors:

  • 404: User not found by userId"User not found!"

DELETE /user/deleteUser

Permanently deletes a workflow user from the system. This is a hard delete — there is no soft-delete or recycle mechanism. The user record is removed entirely.

Request Body:

{ id: string }   // The userId of the user to delete

Response: { message: "User deleted successfully" }

Errors:

  • 404: No user found with the given ID → "User not found!"

GET /user/getUserById/:userId

Retrieves a single user by their unique identifier. Returns the full user object or null if no user exists with the given ID (no explicit 404 is thrown).

Path Params:

ParamTypeDescription
userIdstringThe unique identifier of the user.

Response: IUser or null


GET /user/listWorkflowUsers

Returns all workflow users for the current organization and application. The organizationId and appId are automatically injected into the request by the FM client and used as filters to scope the result to the current tenant and app.

Response: IUser[] — array of all matching user objects.


Model Management

POST /model/insertModel

Upserts a workflow model into the database. If a model with the same ID already exists, it is updated; otherwise a new model is created. This endpoint is the primary way workflow definitions are persisted from the Studio editor.

For models of type "jobScheduler", an additional deploy task is automatically created in the batch job system with status "waiting", which will be picked up by the batch job processor for deployment.

Request Body:

{
model: IModelForWorkflow; // The full model object containing the workflow definition
live: boolean; // true = production deployment, false = draft/preview
}
FieldRequiredDescription
modelYesThe workflow model object containing modelID, type, appId, modelBody, and other metadata.
liveYesIndicates whether this is a live production deployment or a draft save.

Response: string — the upsert operation result.


GET /model/getModel/:id

Fetches a workflow model by its unique identifier. This endpoint implements HTTP conditional caching via ETags to optimize bandwidth. If the client sends an If-None-Match header matching the current model's ETag, a 304 Not Modified response is returned with no body.

The ETag is a composite value built from the model ID, version, update date, and byte length, ensuring it changes whenever the model content is modified.

Path Params:

ParamTypeDescription
idstringThe unique model identifier.

Response Headers:

HeaderValueDescription
Cache-Controlpublic, max-age=288008-hour browser cache lifetime.
ETag"<modelID>-<version>-<updateDate>-<byteLength>"Composite ETag for conditional requests.
VaryAccept-EncodingIndicates response varies by encoding.

Response: The model body content (structure varies by model type).

Errors:

  • 404 (fm3027): Model not found in the database.

Health & Monitoring

GET /healthcheck

Returns the health status of the FormManager service. Reports the service name, package version, last modification date, and the current server time. If version information cannot be read (e.g., missing package.json), the error is captured in the response body rather than thrown — this ensures the health check endpoint itself always remains available.

Response:

{
name: "formManager";
version: string; // e.g., "1.2.3"
modify: Date; // Last modification date of the package
time: Date; // Current server time
error?: string; // Present only if version info could not be read
}

GET /liveness

Minimal liveness probe. Returns only the current server timestamp, confirming the FM process is alive and can handle HTTP requests. Unlike /healthcheck, this endpoint does not check any dependencies (database, filesystem, etc.), making it suitable for Kubernetes liveness probes that should only verify process responsiveness.

Response: { time: Date }


GET /azure/serviceLog/:service

Streams real-time Azure App Service logs via Server-Sent Events (SSE). Acts as a filtered proxy to Azure's Kudu log streaming API. Incoming log lines are filtered by a [CI: <clientId>] pattern — only lines matching the caller's lpLogClientId are forwarded to the client. Azure timestamps are stripped from the output for cleaner display.

A heartbeat mechanism sends empty space characters every ~15 seconds to keep the SSE connection alive during quiet periods when no log lines match.

Path Params:

ParamTypeDescription
service"fm" | "wfm"Which service's logs to stream.

Required Headers:

HeaderDescription
lplogclientidThe client identifier used to filter log lines.
studiotokenAuthentication token for the request.

Response Headers:

HeaderValue
Content-Typetext/event-stream
Cache-Controlno-cache
Connectionkeep-alive

Response: A continuous SSE stream of filtered log lines.

Errors:

  • 400: Missing lplogclientid or service parameter.
  • 500: Missing Azure deployment environment variables.

Settings & Cache

GET /settings/:file

Serves UI settings files (JavaScript) for a given application. The appId is resolved from the request's Referer header through a 3-step fallback chain:

  1. Reads appId directly from the referer URL's query parameters.
  2. If not found, reads processId from referer query params and looks up the appId from the model database.
  3. If still not found, reads the q query param and extracts appId from an encoded composite ID string.

Path Params:

ParamTypeDescription
fileUISettingsFileTypeThe settings file identifier.

Response: JavaScript content served with Content-Type: application/javascript; charset=UTF-8.

Errors:

  • 404 (fm3027): appId could not be resolved from the referer, or the settings model was not found.

GET /external/cache/:cacheType/:cacheTarget/evict

Invalidates cache entries across the system. Inserts a cache invalidation record into the database, which FM and WFM instances poll or react to for clearing their respective caches. This is a coordination-based eviction mechanism — the invalidation is not immediate but propagates through the shared record.

Path Params:

ParamTypeDescription
cacheTypestringThe type of cache to invalidate.
cacheTarget"fm" | "wfm" | "all"Which service(s) should respond to the invalidation.

Allowed cacheType values per target:

TargetAllowed Cache Types
fmapp, all, swagger, endpoint
wfmapp, all, service
allapp, all, swagger, service

Query Params:

ParamTypeRequiredDescription
idstringNoScope invalidation to a specific cache key. Only valid for service and swagger cache types. If omitted, all entries of the given type are invalidated.

Response: { status: "OK" }

Errors:

  • 400: Invalid cacheTarget value, or cacheType not allowed for the given target.

Notifications (FM)

POST /notify

Upserts a notification record in the database. Uses a partial update mechanism — the notification object is flattened into dot-notation keys so that only the provided fields are updated while preserving any existing fields in the document. This allows incremental notification updates without overwriting the entire record.

The notification is keyed by processInstance.processInstanceId, meaning each process instance has exactly one notification record that is continuously updated as the process progresses.

Request Body:

{
notificationObject: {
eventType: NotificationEventTypes;
informList: Array<{
key?: string;
label?: string;
header: string;
display?: boolean;
}>;
processInstance: IProcessInstance; // Must contain processInstanceId
onUs: boolean;
task?: ITask; // Present when onUs = true
}
}

Response: { message: "notified" }

Errors:

  • 400 (wm3019): Missing processInstance.processInstanceId.