Create Approval Policy
POST /base/approval-policies
Group: Base › Approval Policies · Operation ID: createApprovalPolicy
Creates an approval policy with its rule lines, approvers, exclusion lines and notification settings. The response contains only the new policy's id.
The response contains only the new policy's id. Read the record back with GET base/approval-policies/:id for its stored state, including the assigned policyHeaderNumber. Scope the policy to exactly one thing: name an entityId or set isAllEntities. Naming neither fails with MISSING_ENTITY_SCOPE. Rule lines are evaluated independently. A document picks up the approvers of every line it matches, so two plain lines mean either condition brings in its own approvers, not that both must hold. To require several conditions together, send one line with criteria COMPOSITE whose single value is a JSON AND or OR expression. Every rule line needs at least one approver, or the create fails with LINE_WITHOUT_APPROVERS. An active policy whose business object and entity coverage overlap an existing active policy fails with CONFLICTING_ACTIVE_POLICY. Create it with status 'inActive', or deactivate the other first. Send every value as a string, for example ["10000"], not [10000]. Only a line with valueType 'noValue' may omit the value.
Open in the interactive reference
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Api-Version |
header | "v1" |
yes | The API version this request targets, named by its public identifier. Required on every request; omitting it, or naming a version this company does not have, is answered with 400. This document describes version v1. |
Idempotency-Key |
header | string | no | An opaque caller-chosen string (1-255 characters) that makes this request safe to retry. Repeating the same key with an identical request body replays the original response instead of re-executing - but only once that original request succeeded; a failed attempt re-executes on retry. Repeating the key with a different body is answered with 422. A key still in flight is answered with 409 - as is a key whose original request was interrupted before it finished, for a few minutes afterwards, until that attempt is presumed dead. A completed response stays replayable for 24 hours. The key is scoped to the caller, not to the action: reusing it for a different action is answered with 409 or 422 rather than treated independently. On read actions this header is ignored. On write actions that cannot offer the guarantee it is answered with 400 IDEMPOTENCY_NOT_SUPPORTED rather than silently ignored. |
Request body
Required, application/json: createApprovalPolicyInput.
| Field | Type | Required | Description |
|---|---|---|---|
entityId |
string | no | The legal entity the policy applies to. Required unless isAllEntities is true. Leave it out when using isAllEntities. See id on GET base/entities. |
isAllEntities |
boolean | no | Whether the policy applies to every legal entity instead of a single one. When true, leave entityId out. |
businessObject |
string | yes | The code of the business object the policy governs. It must be a registered approval business object, for example 'VendorBill', 'ExpenseReport', 'PurchaseOrder', 'PurchaseRequest' or 'JournalEntry'. An unregistered code fails with UNKNOWN_BUSINESS_OBJECT, which lists the registered codes. It cannot be changed after creation. |
status |
string, one of "active", "inActive" |
no | The lifecycle status of the policy. Defaults to 'active', which makes it eligible for matching immediately. Create it 'inActive' to stage a policy that would otherwise collide with a live one. |
startDate |
string | no | The first date the policy is eligible for matching. Use format YYYY-MM-DD. Omit for no lower bound. |
endDate |
string | no | The last date the policy is eligible for matching. Use format YYYY-MM-DD. Omit for no upper bound. |
submitterCannotApprove |
boolean | no | Whether the document submitter is stopped from approving their own document. When true, their approval does not count toward fulfillment and they are not notified as an approver. Defaults to false. |
approverCommentEnabled |
boolean | no | Whether approvers are prompted for an optional comment at approve time. The comment is stored on the approval trail. Defaults to false. |
notification |
object | no | The notification channels used when this policy's approvers and submitters are told about a document. Omit to leave the tenant's defaults in place. |
notification.emailNotification |
boolean | no | Whether to email approvers. |
notification.nativeNotification |
boolean | no | Whether to raise an in-product notification for approvers. |
notification.slackNotification |
boolean | no | Whether to notify approvers on Slack. |
lines |
array of object | yes | The policy's rule lines. It must not be empty. Rule lines are evaluated independently: a document picks up the approvers of every line it matches. To require several conditions together, use one COMPOSITE line rather than several plain lines. |
lines[].criteria |
string | yes | The document field the rule tests. Use the criteria names the document type publishes, for example 'Vendor', 'Total Amount', 'Department', 'Line Amounts' or 'Upon Creation'. Pass the literal COMPOSITE to combine several conditions in one line. |
lines[].operator |
string | yes | The comparison to apply to the criteria, for example 'Equal', 'Not Equal', 'Greater Than', 'Less Than', 'In', 'Intersect' or the Any and All variants for array fields. Use '-' for a criteria that takes no value. |
lines[].value |
array of string | no | The values to compare against, each as a string, for example ["10000"], not [10000]. Omit only when valueType is 'noValue'. A 'number' valueType takes exactly one numeric element. A 'composite' valueType takes exactly one element holding the JSON expression, for example {"operator":"AND","children":[{"criteriaName":"Vendor","operator":"Equal","value":["Acme"],"valueType":"multivalue"},{"criteriaName":"Total Amount","operator":"Less Than","value":["10000"],"valueType":"number"}]}. |
lines[].valueType |
string, one of "number", "multivalue", "value", "noValue", "arrayField", "composite" |
yes | How to interpret value. 'number' is a single numeric comparison, 'multivalue' one or more discrete values, 'value' a single discrete value, 'noValue' a criteria that takes none such as 'Upon Creation', 'arrayField' the Any, All and Intersect operators over a line-level array, and 'composite' a COMPOSITE expression. |
lines[].step |
number | yes | The approval order, starting at 1. Lines that share a step are approved together, and a higher step is reached only once the lower steps are satisfied. |
lines[].requireAction |
string, one of "All", "Any" |
no | Whether all of the line's approvers must approve, or any one of them is enough. Defaults to 'All'. |
lines[].approvers |
array of object | yes | The approvers for this line. It must not be empty, because a line with no approver can never be satisfied. |
lines[].approvers[].approverId |
number | yes | The approver. For approverType 'employee' or 'supremeApprover' it is the employee, and for 'manager' it is the manager-hierarchy record. It is an integer, not a UUID. There is no endpoint for it. |
lines[].approvers[].approverType |
string, one of "employee", "manager", "supremeApprover" |
yes | The kind of approver. 'employee' names a specific person, 'manager' resolves the submitter's manager through a hierarchy at approval time, and 'supremeApprover' is a person who may approve at any step. |
exclusionLines |
array of object | no | The exclusion lines. A document matching any of them is exempted from the policy entirely, whatever the rule lines say. Omit for no exclusions. |
exclusionLines[].criteria |
string | yes | |
exclusionLines[].operator |
string | yes | |
exclusionLines[].value |
array of string | no | The values to compare against, each as a string. Omit only when valueType is 'noValue'. |
exclusionLines[].valueType |
string, one of "number", "multivalue", "value", "noValue", "arrayField", "composite" |
yes |
Example:
{
"entityId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"isAllEntities": true,
"businessObject": "VendorBill",
"status": "active",
"startDate": "<startDate>",
"endDate": "<endDate>",
"submitterCannotApprove": true,
"approverCommentEnabled": true,
"notification": {
"emailNotification": true
},
"lines": [
{
"criteria": "Total Amount",
"operator": "Greater Than",
"value": [
"10000"
],
"valueType": "number",
"step": 1,
"requireAction": "All",
"approvers": [
{
"approverId": 42,
"approverType": "employee"
}
]
}
],
"exclusionLines": [
{
"criteria": "<criteria>",
"operator": "<operator>",
"value": [
"<value>"
],
"valueType": "number"
}
]
}
Response
200 Success
Under data (createApprovalPolicyOutput):
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The unique identifier for the created approval policy. Every other approval-policy endpoint is keyed by it. |
Response headers
Idempotency-Replayed— Present andtrueonly when this response replays a previously stored one for the same Idempotency-Key, rather than reflecting a live execution. Absent on a live response - never sent asfalse.
Errors
| Code | Status |
|---|---|
MISSING_ENTITY_SCOPE |
400 |
ENTITY_NOT_FOUND |
400 |
UNKNOWN_BUSINESS_OBJECT |
400 |
NO_LINES |
400 |
LINE_WITHOUT_APPROVERS |
400 |
MISSING_LINE_VALUE |
400 |
INVALID_NUMBER_VALUE |
400 |
INVALID_COMPOSITE_EXPRESSION |
400 |
CONFLICTING_ACTIVE_POLICY |
400 |
IDEMPOTENCY_KEY_CONFLICT |
409 |
IDEMPOTENCY_PAYLOAD_MISMATCH |
422 |
Statuses
400— Invalid request or business failure. Codes: MISSING_ENTITY_SCOPE, ENTITY_NOT_FOUND, UNKNOWN_BUSINESS_OBJECT, NO_LINES, LINE_WITHOUT_APPROVERS, MISSING_LINE_VALUE, INVALID_NUMBER_VALUE, INVALID_COMPOSITE_EXPRESSION, CONFLICTING_ACTIVE_POLICY401— Missing or invalid access token403— Not authorized404— Not found409— The Idempotency-Key is still being processed by another request.422— The Idempotency-Key was already used with a different request payload.429— Too many requests500— Internal server error