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

In accounts payable (AP), C3s are the vendor entities that want to send invoices to their customers. They can be individuals or businesses.

In Mercoa, you must create a **vendor** for each vendor that your customers will be paying invoices to through your platform.

# Creating Vendors

Vendors can be created in the [dashboard](https://mercoa.com/dashboard) or with the [create entity](/api-reference/entity/create) endpoint, and linked to a customer using the [link payees](/api-reference/entity/counterparty/add-payees) endpoint.

They will also automatically be created and linked when a customer adds a new vendor through the [React component](/react-library/overview) or embed.

## Creating the Vendor Entity

Using the [create entity](/api-reference/entity/create) endpoint, create a new entity, and make sure the following fields are set:

```ts
{
  isPayee: true, // This marks the entity as able to receive funds
  isPayor: false, // This marks the entity as unable to pay funds
  isCustomer: false // This indicates that you don't have a direct relationship with this entity (aka, they're your customer's vendor)
}
```

This will automatically add the vendor to the `platform` network.

## Capturing Vendor Details

If you don't have the vendor's details, you can use the [generate onboarding link](/api-reference/entity/get-onboarding-link) endpoint to create a link that the vendor can use to provide their details. This link will be valid for 24 hours. You can also use the [send onboarding email](/api-reference/entity/send-onboarding-link) endpoint to send the link to the vendor via email. This link will be valid for 7 days, and will be emailed to the entity email.

You can configure what details are required for the vendor using the [dashboard](https://mercoa.com/dashboard/developers#customizations) or [api](/api-reference/organization/update).

## Adding the Vendor to the Payer as a Counterparty

Once the vendor entity is created, you must link it to the payer.

You can link the vendor to any entity using the [link payees](/api-reference/entity/counterparty/add-payees) endpoint. This will create a relationship between the two entities, and allow the payer to pay the vendor.

For example, if you have a payer Entity with id `ent_8545a84e-a45f-41bf-bdf1-33b42a55812c`, and a vendor Entity with id `ent_21661ac1-a2a8-4465-a6c0-64474ba8181d`, you can link them using the following request

### Request

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

**`Default`**

```curl Default
curl -X POST https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/addPayees \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "payees": [
    "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"
  ],
  "customizations": [
    {
      "counterpartyId": "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
      "accounts": [
        {
          "accountId": "85866843",
          "postalCode": "94105",
          "nameOnAccount": "John Doe"
        }
      ]
    }
  ]
}'
```

**`Default`**

```python Default
import requests

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

payload = {
    "payees": ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"],
    "customizations": [
        {
            "counterpartyId": "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            "accounts": [
                {
                    "accountId": "85866843",
                    "postalCode": "94105",
                    "nameOnAccount": "John Doe"
                }
            ]
        }
    ]
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

**`Default`**

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

const client = new MercoaClient({ token: "YOUR_TOKEN" });
await client.entity.counterparty.addPayees("ent_8545a84e-a45f-41bf-bdf1-33b42a55812c", {
    payees: ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"],
    customizations: [{
            counterpartyId: "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
            accounts: [{
                    accountId: "85866843",
                    postalCode: "94105",
                    nameOnAccount: "John Doe"
                }]
        }]
});

```

**`Default`**

```go Default
package main

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

func main() {

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

	payload := strings.NewReader("{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ],\n  \"customizations\": [\n    {\n      \"counterpartyId\": \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\",\n      \"accounts\": [\n        {\n          \"accountId\": \"85866843\",\n          \"postalCode\": \"94105\",\n          \"nameOnAccount\": \"John Doe\"\n        }\n      ]\n    }\n  ]\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))

}
```

**`Default`**

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

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

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  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ],\n  \"customizations\": [\n    {\n      \"counterpartyId\": \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\",\n      \"accounts\": [\n        {\n          \"accountId\": \"85866843\",\n          \"postalCode\": \"94105\",\n          \"nameOnAccount\": \"John Doe\"\n        }\n      ]\n    }\n  ]\n}"

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

**`Default`**

```java Default
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/addPayees")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ],\n  \"customizations\": [\n    {\n      \"counterpartyId\": \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\",\n      \"accounts\": [\n        {\n          \"accountId\": \"85866843\",\n          \"postalCode\": \"94105\",\n          \"nameOnAccount\": \"John Doe\"\n        }\n      ]\n    }\n  ]\n}")
  .asString();
```

**`Default`**

```php Default
<?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/addPayees', [
  'body' => '{
  "payees": [
    "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"
  ],
  "customizations": [
    {
      "counterpartyId": "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
      "accounts": [
        {
          "accountId": "85866843",
          "postalCode": "94105",
          "nameOnAccount": "John Doe"
        }
      ]
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

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

**`Default`**

```csharp Default
using RestSharp;

var client = new RestClient("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/addPayees");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ],\n  \"customizations\": [\n    {\n      \"counterpartyId\": \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\",\n      \"accounts\": [\n        {\n          \"accountId\": \"85866843\",\n          \"postalCode\": \"94105\",\n          \"nameOnAccount\": \"John Doe\"\n        }\n      ]\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Default`**

```swift Default
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "payees": ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"],
  "customizations": [
    [
      "counterpartyId": "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d",
      "accounts": [
        [
          "accountId": "85866843",
          "postalCode": "94105",
          "nameOnAccount": "John Doe"
        ]
      ]
    ]
  ]
] 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/addPayees")! 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()
```

## Finding Counterparties

Once you have created and linked vendors to the payer, you can use the [get counterparties](/api-reference/entity/counterparty/find-payees) endpoint to find the vendors linked to the payer.

### Request

GET [https://api.mercoa.com/entity/\{entityId}/counterparties/payees](https://api.mercoa.com/entity/\{entityId}/counterparties/payees)

**`Default`**

```curl Default
curl -G https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees \
     -H "Authorization: Bearer <token>" \
     --data-urlencode "name=Big Box" \
     -d paymentMethods=true \
     -d invoiceMetrics=true
```

**`Default`**

```python Default
import requests

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

querystring = {"name":"Big Box","paymentMethods":"true","invoiceMetrics":"true"}

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

**`Default`**

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

const client = new MercoaClient({ token: "YOUR_TOKEN" });
await client.entity.counterparty.findPayees("ent_8545a84e-a45f-41bf-bdf1-33b42a55812c", {
    name: "Big Box",
    paymentMethods: true,
    invoiceMetrics: true
});

```

**`Default`**

```go Default
package main

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

func main() {

	url := "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

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

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

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

}
```

**`Default`**

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

url = URI("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true")

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

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

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

**`Default`**

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

HttpResponse<String> response = Unirest.get("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true")
  .header("Authorization", "Bearer <token>")
  .asString();
```

**`Default`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

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

**`Default`**

```csharp Default
using RestSharp;

var client = new RestClient("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

**`Default`**

```swift Default
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/counterparties/payees?name=Big+Box&paymentMethods=true&invoiceMetrics=true")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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()
```

## Hiding / Archiving Counterparties

If you don't want a counterparty to show up for an Entity in the counterparty search, you can hide them using the [hide payee from search](/api-reference/entity/counterparty/hide-payees) endpoint.

### Request

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

**`Default`**

```curl Default
curl -X POST https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/hidePayees \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "payees": [
    "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"
  ]
}'
```

**`Default`**

```python Default
import requests

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

payload = { "payees": ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"] }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

**`Default`**

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

const client = new MercoaClient({ token: "YOUR_TOKEN" });
await client.entity.counterparty.hidePayees("ent_8545a84e-a45f-41bf-bdf1-33b42a55812c", {
    payees: ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"]
});

```

**`Default`**

```go Default
package main

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

func main() {

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

	payload := strings.NewReader("{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ]\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))

}
```

**`Default`**

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

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

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  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ]\n}"

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

**`Default`**

```java Default
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/hidePayees")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ]\n}")
  .asString();
```

**`Default`**

```php Default
<?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/hidePayees', [
  'body' => '{
  "payees": [
    "ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

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

**`Default`**

```csharp Default
using RestSharp;

var client = new RestClient("https://api.mercoa.com/entity/ent_8545a84e-a45f-41bf-bdf1-33b42a55812c/hidePayees");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"payees\": [\n    \"ent_21661ac1-a2a8-4465-a6c0-64474ba8181d\"\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Default`**

```swift Default
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["payees": ["ent_21661ac1-a2a8-4465-a6c0-64474ba8181d"]] 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/hidePayees")! 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()
```

# Verifying Vendors

If you're using Mercoa's built-in payment rails, your C3 entities will not require a formal KYB verification process. They only need the following information:

## Vendor (C3) KYB Requirements

| Individual     | Business            |
| -------------- | ------------------- |
| Legal name     | Legal business name |
| Phone or email |                     |

# Vendor Portal

The vendor portal provides vendors with a self-service interface to manage their invoices, payment methods, and profile information. This portal allows vendors to:

* View and track their invoices and payment status
* Manage their payment methods (bank accounts, cards, etc.)
* Update their profile and business information
* View vendor credits and balances

## Accessing the Vendor Portal

The vendor portal can be accessed at `/vendors/portal/{orgId}` where `{orgId}` is your organization ID.

### Generating Vendor Portal Links

You can generate secure links for vendors to access their portal using the [generate onboarding link](/api-reference/entity/get-onboarding-link) endpoint with the `redirectToPortal` parameter set to `true`:

```ts
const portalLink = await mercoa.entity.getOnboardingLink(entityId, {
  expiresIn: '30d',
  type: 'payee',
  redirectToPortal: true
});
```

This creates a secure, time-limited link that vendors can use to access their portal without requiring separate login credentials.

### Using the React Component

You can also generate vendor portal links using the `<CounterpartyDetails>` React component. This component provides a built-in interface for managing vendor information and generating portal access links.

Use the `onboardingLinkOptions` prop to configure portal link generation:

```tsx
<CounterpartyDetails
  entityId="your-entity-id"
  counterpartyId="vendor-entity-id"
  onboardingLinkOptions={{
    expiresIn: '30d',
    type: 'payee',
    redirectToPortal: true
  }}
/>
```

This will display a user-friendly interface that allows you to generate and share vendor portal links directly from your application. For more details on using this component, see the [CounterpartyDetails documentation](https://react.mercoa.com/docs/Counterparties/Details).

### Portal Features

#### Invoice Management

* View all invoices (pending, approved, paid, etc.)
* Track payment status and estimated payment dates
* Download invoice PDFs
* Add comments and communicate with payers

#### Payment Methods

* Add and manage bank accounts
* Add and manage payment cards
* Set default payment methods
* Verify payment method ownership

#### Profile Management

* Update business information
* Manage representatives and authorized users
* Upload required documents for KYB verification
* View and update tax information

#### Vendor Credits

* View available credits from overpayments or returns
* Track credit usage and remaining balances
* Apply credits to outstanding invoices

## Integration with AP Workflow

The vendor portal integrates seamlessly with your AP workflow:

1. **Onboarding**: New vendors can complete their profile setup through the portal
2. **Invoice Submission**: Vendors can submit invoices directly through the portal (if enabled)
3. **Payment Tracking**: Vendors can track payment status in real-time
4. **Communication**: Built-in messaging for invoice-related communications
5. **Compliance**: Automatic KYB verification and document collection