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

# Managing Contacts and Locations in Portal.io

> Portal.io contacts represent the clients and companies in your account. Learn how to create, search, and assign contacts and locations to proposals.

Contacts in Portal.io represent the people and companies in your dealer account — the clients you build proposals for. Each contact stores identifying information, communication details, and one or more physical locations. Locations serve two purposes: they let you identify where a job takes place, and they drive tax calculations on proposals.

## What is a contact?

A contact (referred to as a "person" in the API) is a record that stores information about an individual or a company. Every contact has a `partyType` that controls how it is classified:

| `partyType` | Description                                        |
| ----------- | -------------------------------------------------- |
| `Person`    | An individual person. Requires `firstName`.        |
| `Company`   | A company or organization. Requires `companyName`. |

Contacts also have a `contactType` that categorizes their relationship to your business:

| `contactType` | Description             |
| ------------- | ----------------------- |
| `Client`      | A paying customer.      |
| `Employee`    | A member of your team.  |
| `Contractor`  | A subcontractor.        |
| `Other`       | Any other relationship. |

### Contact fields

| Field            | Description                                                |
| ---------------- | ---------------------------------------------------------- |
| `id`             | Unique numeric identifier for the contact.                 |
| `partyType`      | `Person` or `Company`.                                     |
| `contactType`    | Category of the contact relationship.                      |
| `firstName`      | First name (required for individuals).                     |
| `lastName`       | Last name.                                                 |
| `companyName`    | Company name (required when `partyType` is `Company`).     |
| `contactEmail`   | Primary email address.                                     |
| `contactEmailCC` | Additional email addresses copied when a proposal is sent. |
| `contactPhone`   | Primary phone number.                                      |

## Locations

Each contact can have one or more locations (physical addresses). A location stores:

* Street address, suite, city, postal code, state, and country
* A contact name and phone number for that location
* Flags for `isPrimary` (the contact's main address) and `isBilling` (used for billing)

Locations are returned with the `primaryLocation` and `billingLocation` fields on the contact detail response, and you can also retrieve the full list via `GET /public/people/{ContactId}/location`.

### Why locations matter for proposals

Assigning a location to a proposal is how Portal.io determines the applicable sales tax rates. Portal.io uses the `ClientLocation` method: it looks up tax rates for the address and applies them to the taxable parts of the proposal. Until you assign a location, tax totals on the proposal remain zero.

<Note>
  You must assign a contact to a proposal before you can assign a location. Attempting to assign a location to a proposal with no contact returns a **409 Conflict**.
</Note>

## How contacts relate to proposals

A proposal can have one assigned contact and one assigned location. The relationship works like this:

* You assign a contact using `POST /public/proposals/{ProposalId}/contact/{ContactId}`.
* If the contact has exactly one primary location, Portal.io **automatically assigns that location** to the proposal as well.
* If the contact has multiple locations, you assign the location separately using `POST /public/proposals/{ProposalId}/location/{LocationId}`. The location must belong to the already-assigned contact.
* Assigning or changing a location triggers an immediate **tax recalculation** on the proposal.

## Creating contacts and locations

When you create a new contact, `PartyType`, `ContactType`, and `FirstName` are required. `CompanyName` is also required when `PartyType` is `Company`.

```bash theme={null}
curl -X POST https://api.portal.io/public/people \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d PartyType=Person \
  -d ContactType=Client \
  -d FirstName=Jane \
  -d LastName=Smith \
  -d ContactEmail=jane@example.com
```

To add a location to the contact, use `POST /public/people/{ContactId}/location`. `Street` is the only required field, but if you supply a `Country`, `State` is also required.

```bash theme={null}
curl -X POST https://api.portal.io/public/people/42/location \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d Street="123+Main+St" \
  -d City=Austin \
  -d State=TX \
  -d PostalCode=78701 \
  -d Country=US \
  -d IsPrimary=true
```

## Searching for contacts

The `GET /public/people` endpoint returns a paged list of contacts for your account. You can filter by `SearchText` and `ContactTypes`, and control pagination with `PageNumber` and `PageSize` (both default to 1 and 10 respectively).

```bash theme={null}
curl -X GET 'https://api.portal.io/public/people?SearchText=Smith&ContactTypes=Client' \
  -H 'Accept: application/json'
```

## API operations reference

<CardGroup cols={2}>
  <Card title="Search contacts" icon="search">
    `GET /public/people`

    Returns a paged list of contacts. Supports search text, contact type filter, sort, and pagination.
  </Card>

  <Card title="Create contact" icon="user-plus">
    `POST /public/people`

    Creates a new contact in your account.
  </Card>

  <Card title="Get contact details" icon="user">
    `GET /public/people/{ContactId}`

    Returns full contact details including primary and billing locations. Pass `IncludeCounts=true` to include proposal and payment counts.
  </Card>

  <Card title="List contact locations" icon="map-pin">
    `GET /public/people/{ContactId}/location`

    Returns all locations for a contact, ordered with primary first, then billing, then most recently modified.
  </Card>

  <Card title="Create location" icon="map">
    `POST /public/people/{ContactId}/location`

    Adds a new location to an existing contact.
  </Card>

  <Card title="Assign contact to proposal" icon="link">
    `POST /public/proposals/{ProposalId}/contact/{ContactId}`

    Links a contact to a proposal. Auto-assigns the location if the contact has one primary location.
  </Card>

  <Card title="Assign location to proposal" icon="map-pin">
    `POST /public/proposals/{ProposalId}/location/{LocationId}`

    Links a specific location to a proposal and triggers tax recalculation.
  </Card>
</CardGroup>
