# Change the workflow state

Location: [gRPC API](https://developer.m-files.com/APIs/gRPC-API/index.md) > [Guides](https://developer.m-files.com/APIs/gRPC-API/index.md#guides)

HTML version: https://developer.m-files.com/APIs/gRPC-API/guides/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](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/GetWorkflowStatesAndTransitions/index.md)
  with `workflow_id`. Add `current_state` (the object's State value) and `obj_ver` to 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](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/SetProperties/index.md) 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](https://developer.m-files.com/APIs/gRPC-API/Reference/Messages/MFiles.ObjectClass/index.md)); see [Read the vault structure](https://developer.m-files.com/APIs/gRPC-API/guides/vault-structure/index.md).

## 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

```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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/python/recipes/workflow_state.py)*

### C#

```csharp
// 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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/csharp/Recipes/WorkflowState.cs)*

### Go

```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](https://developer.m-files.com/APIs/gRPC-API/downloads/samples/go/recipes/workflow_state/main.go)*

Output against the Sample Vault:

```text
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_state` and `obj_ver`, the response lists the
  states but no transitions, and every state has `can_current_user_select` true because the
  rights cannot be checked.
* **Selectable does not mean allowed.** A state can have preconditions that
  `can_current_user_select` does not check. In the Sample Vault, state 138 ("Waiting for
  Approval") requires the Owner property; moving the test document there fails with
  `0x80040840` "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_visible` false and an empty
  `latest_version`: the user can no longer see the document, and deleting it fails with
  "Access denied" (`0x800407E4`). Check `is_visible` and
  `latest_version.current_user_permissions` in 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](https://developer.m-files.com/APIs/gRPC-API/concepts/workflows/index.md): states, transitions and electronic signatures.
* [Update properties safely](https://developer.m-files.com/APIs/gRPC-API/guides/update-properties/index.md)
