Change the workflow state
An object's workflow and state are property values: Workflow (38) and State (39). Read which states and transitions the workflow has and which of them the user may take, then change the state with a property update.
Calls
- GetWorkflowStatesAndTransitions
with
workflow_id. Addcurrent_state(the object's State value) andobj_verto get the transitions and the user's rights for that object:states:id,name,can_current_user_select.transitions:id,name,from_state_id,to_state_id,can_current_user_select.
- SetProperties with:
- 38 Workflow: a lookup to value list 7, item = workflow ID (to put an object into a workflow);
- 39 State: a lookup to value list 8, item = state ID.
A class can have a default workflow (default_workflow of the
ObjectClass); see Read the vault structure.
Code
The recipe creates a test document that is already in a workflow: it sets Workflow (38) and
State (39) when it creates the document. It then reads the workflow's states and transitions for
that document and sets State to the target state. The defaults are workflow 107 ("Contract
Approval Workflow" in the M-Files Sample Vault), initial state 137 ("Draft") and target state
141 ("Rejected"); pass others as arguments. At the end it deletes the document, unless you pass
--keep.
Python
# The states and transitions of the workflow, with what this user may select for
# this object in its current state.
version = obj_ver(object_id.type, object_id.item_id.internal_id,
created.version_info.version.internal_version)
workflow = objects.GetWorkflowStatesAndTransitions(
ops.GetWorkflowStatesAndTransitionsRequest(
workflow_id=workflow_id,
current_state=lookup(8, initial_state),
obj_ver=version))
for state in workflow.states:
print(f" State {state.id} {state.name}: "
f"selectable {state.can_current_user_select}")
for transition in workflow.transitions:
print(f" Transition {transition.id} {transition.name}: "
f"{transition.from_state_id} -> {transition.to_state_id}, "
f"selectable {transition.can_current_user_select}")
# Change the state by setting property 39 "Workflow State" on the version just read.
updated = objects.SetProperties(ops.SetPropertiesRequest(
obj_ver=version,
allow_modifying_checked_in_object=True,
fail_if_newer_version_exists=True,
properties=[property_value(39, lookup(8, target_state))]))
Source: python/recipes/workflow_state.py
C#
// The states and transitions of the workflow, with what this user may select for
// this object in its current state.
var objVer = ObjVer(objId.Type, objId.ItemId.InternalId, created.VersionInfo.Version.InternalVersion);
var workflow = await objects.GetWorkflowStatesAndTransitionsAsync(new GetWorkflowStatesAndTransitionsRequest
{
WorkflowId = workflowId,
CurrentState = Lookup(8, initialState),
ObjVer = objVer,
});
foreach (var state in workflow.States)
Console.WriteLine($" State {state.Id} {state.Name}: selectable {state.CanCurrentUserSelect}");
foreach (var transition in workflow.Transitions)
{
Console.WriteLine($" Transition {transition.Id} {transition.Name}: {transition.FromStateId} -> " +
$"{transition.ToStateId}, selectable {transition.CanCurrentUserSelect}");
}
// Change the state by setting property 39 "Workflow State" on the version just read.
var updated = await objects.SetPropertiesAsync(new SetPropertiesRequest
{
ObjVer = objVer,
AllowModifyingCheckedInObject = true,
FailIfNewerVersionExists = true,
Properties = { Property(39, Lookup(8, targetState)) },
});
Source: csharp/Recipes/WorkflowState.cs
Go
// The states and transitions of the workflow, with what this user may select for this
// object in its current state.
objVer := mfilesclient.ObjVer(objID.GetType(), objID.GetItemId().GetInternalId(),
created.GetVersionInfo().GetVersion().GetInternalVersion())
workflow, err := objects.GetWorkflowStatesAndTransitions(ctx,
&mfiles.GetWorkflowStatesAndTransitionsRequest{
WorkflowId: workflowID,
CurrentState: mfilesclient.Lookup(8, initialState),
ObjVer: objVer,
})
if err != nil {
return err
}
for _, state := range workflow.GetStates() {
fmt.Printf(" State %d %s: selectable %v\n", state.GetId(), state.GetName(),
state.GetCanCurrentUserSelect())
}
for _, transition := range workflow.GetTransitions() {
fmt.Printf(" Transition %d %s: %d -> %d, selectable %v\n", transition.GetId(),
transition.GetName(), transition.GetFromStateId(), transition.GetToStateId(),
transition.GetCanCurrentUserSelect())
}
// Change the state by setting property 39 "Workflow State" on the version just read.
updated, err := objects.SetProperties(ctx, &mfiles.SetPropertiesRequest{
ObjVer: objVer,
AllowModifyingCheckedInObject: true,
FailIfNewerVersionExists: true,
Properties: []*mfiles.PropertyValue{
mfilesclient.PropertyValue(39, mfilesclient.Lookup(8, targetState)),
},
})
if err != nil {
return err
}
Source: go/recipes/workflow_state/main.go
Output against the Sample Vault:
Created [docs-test] Python workflow-state (ID 613, version 1, modified 2026-10-31 14:55:59 UTC)
State 137 Draft: selectable True
State 138 Waiting for Approval: selectable True
State 139 Approved: selectable False
State 140 Expired: selectable False
State 141 Rejected: selectable True
Transition 5 Rejected: 137 -> 141, selectable True
Transition 11 Waiting for Approval: 137 -> 138, selectable True
State is now Rejected (ID 141), version 2
Deleted object 0-613
Pitfalls
- Transitions need the object. Without
current_stateandobj_ver, the response lists the states but no transitions, and every state hascan_current_user_selecttrue because the rights cannot be checked. - Selectable does not mean allowed. A state can have preconditions that
can_current_user_selectdoes not check. In the Sample Vault, state 138 ("Waiting for Approval") requires the Owner property; moving the test document there fails with0x80040840"The preconditions specified for the new state are not satisfied.", although the transition 137 -> 138 is selectable. That is why the recipe's default target is 141. - A state can take your rights away. States can change the object's permissions. In the
Sample Vault, moving a new document into the first state of workflow 102 ("Received, awaiting
checking") succeeds, but the response has
object_info.is_visiblefalse and an emptylatest_version: the user can no longer see the document, and deleting it fails with "Access denied" (0x800407E4). Checkis_visibleandlatest_version.current_user_permissionsin the response. - State IDs and transition IDs are separate ID spaces, and both differ between vaults.
- Each state change creates a new object version (version 2 above).
See also
- Workflows: states, transitions and electronic signatures.
- Update properties safely