Introduction
Transitions
Transitions are a part of the status flow implementation. A transition references a specific function on a controller; these specific functions are used to transition an entity between states.
A Transition endpoint only accepts one argument, an Entity ID. Transition methods are implemented on each respective entity controller. For example, a Transition for SupplierInvoice is defined as (method=Approve, Controller=SupplierInvoiceController, Entity=SupplierInvoice) - essentially POST api/biz/SupplierInvoices/1?action=Assign.
In order for any given Transition to execute successfully, a corresponding TransitionFlow must be defined and configured for the current status code on the entity which is being transitioned.
Transitions are an important component in approval flows. Tasks reference transitions to configure what happens when the flow has concluded - once approved or rejected.
There is no support for inserting your own or modifying existing transitions.
List available transitions
GET api/biz/transitions?top=2
[
{
"ID": 1,
"UpdatedAt": null,
"MethodName": "activate",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "UserCtrl",
"EntityType": "User",
"CreatedAt": null
},
{
"ID": 2,
"UpdatedAt": null,
"MethodName": "inactivate",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "UserCtrl",
"EntityType": "User",
"CreatedAt": null
}
]
Execute a transition
POST api/biz/users/1?action=activate
A successful transition returns 204 No Content.
A failed transition returns 400 Bad Request, based on TransitionFlow and the current state of the entity.
TransitionFlow
TransitionFlow outlines the resulting status code once executed, and which transitions are valid given the entity's status code. There is no endpoint, and you are not able to modify these.
List TransitionFlows
You can retrieve them using our statistics endpoint. The following request will retrieve all TransitionFlow items, with information about valid To-/From Status. Entries with IsDepricated set to true are no longer active and should be ignored.
GET api/statistics?model=TransitionFlow&select=TransitionFlow.*%2C%20ToStatus.Description%2C%20ToStatus.StatusCode%2CFromStatus.Description%2CfromStatus.StatusCode&expand=FromStatus%2C%20ToStatu
{
"Data": [
{
"CreatedAt": null,
"EntityType": "User",
"ExpiresDate": null,
"ToStatusID": 9,
"CreatedBy": null,
"FromStatusID": 10,
"UpdatedBy": null,
"IsDepricated": null,
"UpdatedAt": null,
"TransitionID": 1,
"ID": 1,
"ToStatusDescription": "Active",
"ToStatusStatusCode": 110001,
"FromStatusDescription": "Inactive",
"fromStatusStatusCode": 110002
},
{
"CreatedAt": null,
"EntityType": "User",
"ExpiresDate": null,
"ToStatusID": 10,
"CreatedBy": null,
"FromStatusID": 9,
"UpdatedBy": null,
"IsDepricated": null,
"UpdatedAt": null,
"TransitionID": 2,
"ID": 2,
"ToStatusDescription": "Inactive",
"ToStatusStatusCode": 110002,
"FromStatusDescription": "Active",
"fromStatusStatusCode": 110001
}
],
"Success": true
}
ApprovalRule
A ApprovalRule is a stored grouping of users which can be reused to create approval flows. Within a rule, we can define one or more steps where each step can contain one or more users. Each step in a ApprovalRule is run sequentially, and only one step may be active at any given time.
Each step can have the following configuration
- A limit, which may trigger or skip the step. If the current step's limit is greater than the assigned entity's amount field (e.g TaxInclusiveAmount on SupplierInvoice); skip the step.
- One or all users on the step must approve. If set to one, the step will complete once a single user approves.
- Dynamic dimension approvers ( Project / Department ). If enabled, the assigned entity's Project and Department will be looked up, and the Project-/DepartmentApprover is assigned. Note that there will not be any assignment if the assigned entity does not contain a Project / Department reference.
Global Rules
If you create a ApprovalRule that only contains a single step with a limit, it is considered a global rule. Global rules may trigger automatically on certain transitions/endpoints
on SupplierInvoiceController:
- sendForPayment
- sendForPaymentWithPaymentData
- journal
- try-auto-assign
Creating a ApprovalRule
To create the following ApprovalRule, where:
- Step 1
- Only one must approve, user 3 or user 4
- Step 2
- All must approve, lookup project & department found on entity which is being assigned, assign to user found in Department-/ProjectApprover
POST api/biz/approvalrules
Payload
{
"Steps": [
{
"StepNumber": 1,
"ApprovalType": 1,
"Users": [
{ "UserID": 3 },
{ "UserID": 4 }
],
"LookupDepartmentApprover": false,
"LookupProjectApprover": false
},
{
"StepNumber": 2,
"ApprovalType": 0,
"Users": [],
"LookupDepartmentApprover": true,
"LookupProjectApprover": true
}
],
"Description": "My Example Rule"
}
Approval Flows
Key classes for the workflow
Task
Tasks serve as the base entity for orchestrating approval flows.
A single task can involve multiple Approvals, which operates concurrently and/or in a defined sequence by additionally providing TaskApprovalPlans.
An entity may have only one active Task at any given time.
Once a task is 'completed', one of the configured transitions is executed - based on the outcome (approved or rejected)
Approval
Approvals represents assignment in a task. A single task may have multiple approvals and one approval only has a parent Task.
An Approval points to a user or role, in which the user or any user with the corresponding role, must approve or reject.
Once all active Approvals are approved or a single rejection has occcured, the flow can progress further. Approvals are always included as sub-elements on Task creation.
Get Active approvals for specific user ID
GET api/biz/approvals?filter=UserID+eq+1+and+statuscode+eq+50120
[
{
"Thresholds": null,
"TaskID": 68,
"Task": null,
"SharedRoleId": null,
"UserID": 1,
"User": null,
"Amount": 13.0,
"CurrencyCode": "NOK",
"ApprovalType": 1,
"StatusCode": 50120,
"ID": 71,
},
{
"Thresholds": null,
"TaskID": 70,
"Task": null,
"SharedRoleId": null,
"UserID": 1,
"User": null,
"Amount": 123123.0,
"CurrencyCode": "NOK",
"ApprovalType": 1,
"StatusCode": 50120,
"ID": 73,
}
]
Approval Actions
Approval actions validates whether or not the requesting user is allowed to modify the Approval. One of the following statements must be true
- Requesting user ID equals UserID on Approval
- Requesting user has the role referenced on Approval.SharedRoleID
- Requesting user is substitute for Approval.UserID
Approve an active approval
POST api/biz/approvals/75?action=approve
Approves the approval with ID 75.
Description: 1. If all current approvals has ApprovalType.One; update all other Approvals' to Indirectly Approved. 2. Create new Approvals based on ApprovalPlans for the next step, and notify assigned users if any. 3. If no more approvals are active, execute the transition referenced in Task.SharedApproveTransitionID
When a task has ApprovalType.Custom, no approvals will be created or set to indirectly approved regardless of state. The transition referenced in Task.SharedApproveTransitionID will be executed ( Assumes the approve transition endpoint will handle it instead )
Reject an active approval
POST api/biz/approvals/75?action=reject
Rejects the Approval with ID 75
Description: 1. Executes transition found on `Task.SharedRejectTransitionID` 2. If `task.RejectStatusCode` is defined, set this on the rejected entity. 3. Delete all remaining active approvals
TaskApprovalPlan
TaskApprovalPlan is a blueprint for a future Approval. Once the last Approval in a task has been approved, new Approvals may be created using TaskApprovalPlans found on the parent Task. These can utilize StepNumber on each TaskApprovalPlan for sequential approvals within a Task. StepNumbers are expected to be in ascending order (lowest first).
A Task may have many TaskApprovalPlans, but one TaskApprovalPlan belongs to a single Task.
ApprovalSubstitutes
Control and manage substitutes. Allow a user to approve or reject on behalf of another, within a given timeperiod.
Create an ApprovalSubstitute
User 3 is allowed to approve on behalf of User 1, betweeen 2025-04-8 and 2025-04-09
POST api/biz/approvalsubstitutes
Payload
{
"SubstituteUserID": "3",
"UserID": "1",
"FromDate": "2025-04-08",
"ToDate": "2025-04-09"
}
Delete an ApprovalSubstitute
DELETE api/biz/approvalsubstitutes/2
Creation of approval flows
Entities relevant for approval flows, such as SupplierInvoice, has their own controller endpoints to manage and start these.
Entities with controller endpoints for flows
- PayrollRun
- SupplierInvoice
- WorkItemGroup
It is preferred to use these, because
- Creation of Task, Approvals and ApprovalPlans is handled for you.
- The entity statuscode is handled automatically. e.g., SupplierInvoice status code is set to 'ForApproval'.
- Assigned users are notified (email, signalR)
- You can assign using Teams, ApprovalRule or Users directly. Note that only the ApprovalRule option supports more complex flows such as sequential steps with multiple approvers.
Create Approval Flow for SupplierInvoice
Use the SupplierInvoiceController's approval flow actions
Create flow and assign users provided
POST api/biz/supplierinvoices/105?action=assign-to
Payload Only use ONE of the following keys
{
"ApprovalRuleID": 17,
"TeamIDs": [1, 2, 3],
"UserIDs": [4, 5, 6]
}
Reassign current approval flow
Use when the SupplierInvoice is already in a flow but you wish to cancel it and create a new one.
POST api/biz/supplierinvoices/105?action=reAssign-to
Payload Only use ONE of the following keys
{
"ApprovalRuleID": 17,
"TeamIDs": [1, 2, 3],
"UserIDs": [4, 5, 6]
}
You can perform creation of a flow manually:
A manual version of POST api/biz/supplierinvoices/14?action=assign-to
where the following will be configured
- First User 1 is assigned and must approve then User 2 is assigned and must approve.
- If any of the two users rejected - execute the rejectInvoice transition
- if both users have approved - execute the Approve transition
1. Find applicable Transitions for SupplierInvoice:
GET api/biz/transitions?filter=EntityType%20eq%20'SupplierInvoice
[
{
"ID": 3,
"UpdatedAt": null,
"MethodName": "assign",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 4,
"UpdatedAt": null,
"MethodName": "reAssign",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 5,
"UpdatedAt": null,
"MethodName": "approve",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 6,
"UpdatedAt": null,
"MethodName": "rejectInvoice",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 7,
"UpdatedAt": null,
"MethodName": "journal",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 8,
"UpdatedAt": null,
"MethodName": "sendForPayment",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 9,
"UpdatedAt": null,
"MethodName": "pay",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 10,
"UpdatedAt": null,
"MethodName": "rejectAssignment",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 11,
"UpdatedAt": null,
"MethodName": "finish",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 12,
"UpdatedAt": null,
"MethodName": "restore",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 13,
"UpdatedAt": null,
"MethodName": "cancelApprovement",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
},
{
"ID": 73,
"UpdatedAt": null,
"MethodName": "revertFinish",
"UpdatedBy": null,
"CreatedBy": null,
"Controller": "SupplierInvoiceController",
"EntityType": "SupplierInvoice",
"CreatedAt": null
}
]
2. Find ModelID for SupplierInvoice
GET api/biz/models?filter=Name%20eq%20'SupplierInvoice'
[
{
"Shared": false,
"ID": 162,
"UpdatedAt": null,
"Description": null,
"LabelPlural": null,
"UpdatedBy": null,
"CreatedBy": null,
"Admin": false,
"Name": "SupplierInvoice",
"Label": "SupplierInvoice",
"CreatedAt": null
}
]
3. Set invoice to assigned using assign action:
POST api/biz/SupplierInvoices/14?action=assign
4. Create the task with included Approvals and TaskApprovalPlans
POST api/biz/tasks
Payload
{
"UserID": 1,
"SharedApproveTransitionId": 5,
"SharedRejectTransitionId": 6,
"Type": 1,
"EntityID": 14,
"ModelID": 162,
"Title": "Approval of SupplierInvoice with ID 14",
"Approvals": [
{
"UserID": 1,
"Amount": "1234",
"CurrencyCode": "NOK",
}
],
"ApprovalPlan": [
{
"UserID": 3,
"StepNumber": 2,
"Limit": 0,
"Amount": 1234,
"CurrencyCode": "NOK",
"StatusCode": 10001,
"ApprovalType": 0,
}
]
}
TransitionThresholds
A TransitionThreshold lets you configure a Role, Approve/Reject Transitions and a comparison for the transition entity. The TransitionThreshold applies to the transition which is referenced on SharedApproveTransitionId.
Whenever this Transition is executed, all TransitionThresholds which references it, are validated against the current entity. When a TransitionThreshold check fails, and the requesting user does not have the specified SharedRoleID, an approval flow is instantiated with the SharedRoleID provided.
This functionality is limited as you are not able to specify users, teams or rules - only a Role. There is no support for sequential steps.
Create a TransitionThreshold
Create a transition threshold for the SupplierInvoice Journal transition. Which may trigger a approval flow using the Sales.Admin role, whenever the SupplierInvoice Journal transition is executed and the invoice.Requsition property is equal to "sales"
In cases where the requesting user has the role that is referenced, which in our case is Sales.Admin; the approval flow will not be created, as it is implicitly approved.
POST /api/biz/thresholds
Payload
{
"PropertyName": "Requisition",
"Operator": 8,
"Value": "sales",
"SharedApproveTransitionId": 7,
"SharedRejectTransitionId": 6,
"SharedRoleId": 24,
}
ApprovalRule entity linking
ApprovalRuleLinkGroup and ApprovalRuleLink connects ApprovalRules to Departments, Projects and/or Suppliers. This functionality is used to enumerate which ApprovalRule should be used to assign an invoice, depending on the invoice's Project/Department/Supplier.
ApprovalRuleLinkGroup
Parent table for ApprovalRuleLink, defines groupings, priority and which entities that can be linked.
Example request to create two groups.
- A group for Projects with the highest priorty, where Project with ID 2, points to ApprovalRule with ID 17.
- A second group for Suppliers which is prioritized after the projects group, where Supplier with ID 12, points to ApprovalRule with ID 13.
PUT api/biz/ApprovalRuleLinkGroups/?action=bulk-save
Payload
[
{
"SourceEntity": 1,
"Links": [
{
"SourceInstanceID": 2,
"SourceEntity": 1,
"ApprovalRuleID": 17,
}
],
"Priority": 0
},
{
"SourceEntity": 0,
"Links": [
{
"SourceInstanceID": 12,
"SourceEntity": 0,
"ApprovalRuleID": 13,
}
],
"Priority": 1
}
]
SourceEntity supports following values which represents different tables Supplier = 0,
Project = 1,
Department = 2
Priority sets the order in which links are returned when using the action get-approvalruleid, where 0 would be the highest priority.
ApprovalRuleLink
Link entities to ApprovalRuleID. An ApprovalRuleLink always points to a ApprovalRuleLinkGroup, ApprovalRuleID and SourceInstanceID with SourceEntity.
Adding ApprovalRuleLinks
Assuming a group exists already, you can create links directly. This endpoint allows for insert or update, and expects all provided links in the same request to be of the same ApprovalRuleLinkGroupID and SourceEntity
POST api/biz/ApprovalRuleLinks?action=insert-or-update-links-in-linkgroup
Payload
[
{
"ApprovalRuleLinkGroupID": 1,
"SourceInstanceID": 3,
"SourceEntity": 1,
"ApprovalRuleID": 17
},
{
"ApprovalRuleLinkGroupID": 1,
"SourceInstanceID": 1,
"SourceEntity": 1,
"ApprovalRuleID": 17
}
]
Enumerate ApprovalRuleID
GET api/biz/ApprovalRuleLinks?action=get-approvalruleid&projectID=1&supplierID=12
this will return the ID of a ApprovalRule which matched the provided information, if any.
With the configuration listed above, the endpoint would return 17; ApprovalRuleLinkGroup with SourceEntity = 1 (Project) has priority = 0 and contains a link between ProjectID: 1 and ApprovalRuleID: 17