> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cleo-pay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Contact — POST /v1/contacts API Reference

> POST /v1/contacts creates a new payee or business contact. Optionally include bank account and tax info to make the contact payment-ready on creation.

Use this endpoint to create a new contact in your account. Contacts can represent either a `payee` (a vendor you manage) or a `business` (an independent Cleo business). To make a contact payment-ready at creation time, include `bankAccount` and `taxInfo` in the request body.

## Base URL

```
POST https://api.cleo-pay.com/v1/contacts
```

## Authentication

All requests must include a Bearer token in the `Authorization` header.

```http theme={null}
Authorization: Bearer <token>
```

## Request Body

<ParamField body="displayName" type="string" required>
  The display name for the contact. This is the primary identifier shown in the Cleo Pay dashboard.
</ParamField>

<ParamField body="emails" type="string[]">
  List of email addresses for the contact. Maximum 5 addresses. Only applicable to `payee`-type contacts — this field is ignored for `business`-type contacts.
</ParamField>

<ParamField body="phone" type="string">
  Phone number for the contact, in any standard format. Optional.
</ParamField>

<ParamField body="address" type="object">
  Mailing address for the contact. Optional.

  <Expandable title="address fields">
    <ParamField body="streetOne" type="string" required>
      Primary street address line.
    </ParamField>

    <ParamField body="streetTwo" type="string">
      Secondary street address line (e.g., suite or unit number). Optional.
    </ParamField>

    <ParamField body="city" type="string" required>
      City name.
    </ParamField>

    <ParamField body="stateCode" type="string" required>
      Two-letter US state code (e.g., `CA`, `NY`).
    </ParamField>

    <ParamField body="zipCode" type="string" required>
      US ZIP code.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="bankAccount" type="object">
  Bank account details for the contact. Providing this field makes the contact eligible to receive ACH payments. All fields within this object are **write-only** — they are never returned in API responses. Only `last4` is returned after creation.

  <Expandable title="bankAccount fields">
    <ParamField body="accountNumber" type="string">
      Full bank account number. **Write-only.**
    </ParamField>

    <ParamField body="routingNumber" type="string">
      Nine-digit ABA routing number. **Write-only.**
    </ParamField>

    <ParamField body="accountType" type="string">
      Type of bank account. Accepted values: `checking`, `savings`.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="taxInfo" type="object">
  Tax identification information for the contact. Providing this field is required for 1099 reporting. The full tax ID is **write-only** — only the last four digits are returned as `taxIdMask` after creation.

  <Expandable title="taxInfo fields">
    <ParamField body="legalName" type="string" required>
      Legal name as it appears on tax filings.
    </ParamField>

    <ParamField body="taxIdType" type="string" required>
      Type of tax ID. Accepted values: `ein` (Employer Identification Number), `ssn` (Social Security Number).
    </ParamField>

    <ParamField body="taxId" type="string" required>
      Full tax identification number. **Write-only** — only the last four digits are returned as `taxIdMask` in all subsequent responses.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="allowDuplicates" type="boolean" default="false">
  When `false` (the default), Cleo Pay checks for an existing contact with a matching name or email and returns an error if a potential duplicate is found. Set this to `true` to bypass the duplicate-contact guard and force creation.
</ParamField>

<Note>
  The `bankAccount.accountNumber`, `bankAccount.routingNumber`, and `taxInfo.taxId` fields are strictly write-only. They are accepted on creation but **never returned** in any API response. Use `bankAccount.last4` and `taxInfo.taxIdMask` to confirm the values that were saved.
</Note>

## Response

Returns the created `Contact` object with HTTP status `201 Created`.

<ResponseField name="id" type="string">
  Unique identifier for the newly created contact.
</ResponseField>

<ResponseField name="type" type="string">
  The contact type: `payee` or `business`.
</ResponseField>

<ResponseField name="status" type="string">
  Initial status of the contact. Newly created contacts have a status of `active`.
</ResponseField>

<ResponseField name="displayName" type="string | null">
  The display name provided in the request.
</ResponseField>

<ResponseField name="emails" type="string[]">
  Email addresses associated with the contact. Empty for `business`-type contacts.
</ResponseField>

<ResponseField name="phone" type="string | null">
  Phone number, if provided.
</ResponseField>

<ResponseField name="paymentDirection" type="string">
  Allowed payment direction: `bidirectional`, `can_pay`, `can_be_paid`, or `none`.
</ResponseField>

<ResponseField name="netTerms" type="number | null">
  Net payment terms in days, if configured.
</ResponseField>

<ResponseField name="bankAccount" type="object | null">
  Summary of the linked bank account.

  <Expandable title="bankAccount fields">
    <ResponseField name="last4" type="string">
      Last four digits of the bank account number.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="taxInfo" type="object | null">
  Tax information summary.

  <Expandable title="taxInfo fields">
    <ResponseField name="legalName" type="string | null">
      Legal name provided at creation.
    </ResponseField>

    <ResponseField name="taxIdType" type="string | null">
      Type of tax ID: `ein` or `ssn`.
    </ResponseField>

    <ResponseField name="taxIdMask" type="string | null">
      Last four digits of the tax ID. The full tax ID is never returned.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="paymentReady" type="boolean">
  Whether the contact can currently receive payments. This is `true` when a valid bank account is linked.
</ResponseField>

<ResponseField name="createdAt" type="datetime">
  ISO 8601 timestamp of contact creation.
</ResponseField>

<ResponseField name="updatedAt" type="datetime | null">
  ISO 8601 timestamp of the most recent update. `null` immediately after creation.
</ResponseField>

<ResponseField name="archivedAt" type="datetime | null">
  ISO 8601 timestamp of archival. `null` for newly created contacts.
</ResponseField>

## Example Request

```bash curl theme={null}
curl --request POST \
  --url "https://api.cleo-pay.com/v1/contacts" \
  --header "Authorization: Bearer <token>" \
  --header "Content-Type: application/json" \
  --data '{
    "displayName": "Acme Supplies LLC",
    "emails": ["billing@acmesupplies.com"],
    "phone": "+15550001234",
    "address": {
      "streetOne": "123 Commerce Drive",
      "city": "Austin",
      "stateCode": "TX",
      "zipCode": "78701"
    },
    "bankAccount": {
      "accountNumber": "123456789012",
      "routingNumber": "021000021",
      "accountType": "checking"
    },
    "taxInfo": {
      "legalName": "Acme Supplies LLC",
      "taxIdType": "ein",
      "taxId": "12-3456789"
    },
    "allowDuplicates": false
  }'
```

## Example Response

```json 201 theme={null}
{
  "id": "cnt_01hq1z2k3m4n5p6q7r8s9t0u",
  "type": "payee",
  "status": "active",
  "displayName": "Acme Supplies LLC",
  "emails": ["billing@acmesupplies.com"],
  "phone": "+15550001234",
  "paymentDirection": "can_be_paid",
  "netTerms": null,
  "bankAccount": {
    "last4": "2012"
  },
  "taxInfo": {
    "legalName": "Acme Supplies LLC",
    "taxIdType": "ein",
    "taxIdMask": "6789"
  },
  "paymentReady": true,
  "createdAt": "2024-04-10T12:00:00Z",
  "updatedAt": null,
  "archivedAt": null
}
```

<Tip>
  To add a contact without bank or tax details now and complete them later, omit `bankAccount` and `taxInfo` from the request. You can update the contact's display name, emails, phone, address, and net terms via a subsequent `PATCH /v1/contacts/{contactId}` call.
</Tip>
