> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.mercoa.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.mercoa.com/_mcp/server.

## [API Reference](/api-reference/entity/approval-policy)

Approval Policies enable your users to set up an approval process for managing and paying any invoice they receive.

## Creating Approval Policies

To create an Approval Policy:

1. [Create users](/common-concepts/entity-users) within an entity and optionally assign them roles.
2. Create the Approval Policy rule

## Creating Approval Policy rules

Approval Policies can be created using the [API](/api-reference/entity/approval-policy/create) or through the [admin dashboard](https://mercoa.com/dashboard/payers) by clicking on the `Approval Policy` tab within an entity.

Approval Policies consist of a trigger for the policy and a list of users that must approve the invoice. You can stack multiple Approval Policies on top of each other.

### Approval Policy Triggers

The "Trigger" field allows you to select what conditions trigger this approval policy.

You can select the following to trigger an approval policy:

* Invoice Amounts (greater than)
* Invoice Metadata
* Invoice Vendors

### Number of Approvers

Each approval policy can have a different number of approvers. You can set the number of approvers needed for each approval policy by using the "Number of Approvers" field.

### Approval Policy Rules

The "Approval Policy Rules" field allows you to select which users are the approvers for this approval policy. You can select users based on their roles or by their user IDs. Roles are useful when you want any user with a specific role to be able to approve an invoice. User IDs are useful when you want to specify exactly which users can approve an invoice.

### Example Approval Policy

In this example, we create an approval policy that triggers when an invoice is received and the invoice amount is greater than \$100. We set the number of approvers to 2 and let anyone with the "Controller" role or "Admin" role approve the invoice.

### Request

POST [https://api.mercoa.com/entity/\{entityId}/approval-policy](https://api.mercoa.com/entity/\{entityId}/approval-policy)

**`AlwaysTrigger`**

```curl AlwaysTrigger
curl -X POST https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "trigger": [],
  "rule": {
    "type": "approver",
    "identifierList": {
      "type": "userList",
      "value": [
        "usr_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"
      ]
    },
    "numApprovers": 2
  },
  "upstreamPolicyId": "root"
}'
```

**`AlwaysTrigger`**

```python AlwaysTrigger
import requests

url = "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy"

payload = {
    "trigger": [],
    "rule": {
        "type": "approver",
        "identifierList": {
            "type": "userList",
            "value": ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
        },
        "numApprovers": 2
    },
    "upstreamPolicyId": "root"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

**`AlwaysTrigger`**

```typescript AlwaysTrigger
import { MercoaClient } from "@mercoa/javascript";

const client = new MercoaClient({ token: "YOUR_TOKEN" });
await client.entity.approvalPolicy.create("ent_8545a84e-a45f-41bf-bdf1-33b42a55812c", {
    trigger: [],
    rule: {
        type: "approver",
        numApprovers: 2,
        identifierList: {
            type: "userList",
            value: ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
        }
    },
    upstreamPolicyId: "root"
});

```

**`AlwaysTrigger`**

```go AlwaysTrigger
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy"

	payload := strings.NewReader("{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`AlwaysTrigger`**

```ruby AlwaysTrigger
require 'uri'
require 'net/http'

url = URI("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}"

response = http.request(request)
puts response.read_body
```

**`AlwaysTrigger`**

```java AlwaysTrigger
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}")
  .asString();
```

**`AlwaysTrigger`**

```php AlwaysTrigger
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy', [
  'body' => '{
  "trigger": [],
  "rule": {
    "type": "approver",
    "identifierList": {
      "type": "userList",
      "value": [
        "usr_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"
      ]
    },
    "numApprovers": 2
  },
  "upstreamPolicyId": "root"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

**`AlwaysTrigger`**

```csharp AlwaysTrigger
using RestSharp;

var client = new RestClient("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`AlwaysTrigger`**

```swift AlwaysTrigger
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "trigger": [],
  "rule": [
    "type": "approver",
    "identifierList": [
      "type": "userList",
      "value": ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
    ],
    "numApprovers": 2
  ],
  "upstreamPolicyId": "root"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

## Auto-Assign Approvers

Approval policies can be set to automatically assign approvers when a user submits an invoice for approval. If the approval rule is set to `userList` and the number of approvers needed is equal to the number of users that are available to approve, it automatically assigns those users.

### Example

### Request

POST [https://api.mercoa.com/entity/\{entityId}/approval-policy](https://api.mercoa.com/entity/\{entityId}/approval-policy)

**`AlwaysTrigger`**

```curl AlwaysTrigger
curl -X POST https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "trigger": [],
  "rule": {
    "type": "approver",
    "identifierList": {
      "type": "userList",
      "value": [
        "usr_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"
      ]
    },
    "numApprovers": 2
  },
  "upstreamPolicyId": "root"
}'
```

**`AlwaysTrigger`**

```python AlwaysTrigger
import requests

url = "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy"

payload = {
    "trigger": [],
    "rule": {
        "type": "approver",
        "identifierList": {
            "type": "userList",
            "value": ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
        },
        "numApprovers": 2
    },
    "upstreamPolicyId": "root"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

**`AlwaysTrigger`**

```typescript AlwaysTrigger
import { MercoaClient } from "@mercoa/javascript";

const client = new MercoaClient({ token: "YOUR_TOKEN" });
await client.entity.approvalPolicy.create("ent_8545a84e-a45f-41bf-bdf1-33b42a55812c", {
    trigger: [],
    rule: {
        type: "approver",
        numApprovers: 2,
        identifierList: {
            type: "userList",
            value: ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
        }
    },
    upstreamPolicyId: "root"
});

```

**`AlwaysTrigger`**

```go AlwaysTrigger
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy"

	payload := strings.NewReader("{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

**`AlwaysTrigger`**

```ruby AlwaysTrigger
require 'uri'
require 'net/http'

url = URI("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}"

response = http.request(request)
puts response.read_body
```

**`AlwaysTrigger`**

```java AlwaysTrigger
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}")
  .asString();
```

**`AlwaysTrigger`**

```php AlwaysTrigger
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy', [
  'body' => '{
  "trigger": [],
  "rule": {
    "type": "approver",
    "identifierList": {
      "type": "userList",
      "value": [
        "usr_8545a84e-a45f-41bf-bdf1-33b42a55812c",
        "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"
      ]
    },
    "numApprovers": 2
  },
  "upstreamPolicyId": "root"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

**`AlwaysTrigger`**

```csharp AlwaysTrigger
using RestSharp;

var client = new RestClient("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"trigger\": [],\n  \"rule\": {\n    \"type\": \"approver\",\n    \"identifierList\": {\n      \"type\": \"userList\",\n      \"value\": [\n        \"usr_8545a84e-a45f-41bf-bdf1-33b42a55812c\",\n        \"usr_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n      ]\n    },\n    \"numApprovers\": 2\n  },\n  \"upstreamPolicyId\": \"root\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`AlwaysTrigger`**

```swift AlwaysTrigger
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "trigger": [],
  "rule": [
    "type": "approver",
    "identifierList": [
      "type": "userList",
      "value": ["usr_8545a84e-a45f-41bf-bdf1-33b42a55812c", "usr_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
    ],
    "numApprovers": 2
  ],
  "upstreamPolicyId": "root"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/approval-policy")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```