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

# Contacts API — Create, Retrieve, and Update Contacts

> Manage individual contacts associated with companies. Supports listing with pagination and filters, creating, retrieving, and patching contact records.

Contacts represent individual people inside a company. Each contact must be linked to a company via `companyId`. You can attach a role, mark one contact as primary, and store arbitrary key-value data in `customFields`.

<Note>
  All contacts endpoints are scoped to your tenant. Pass your API key in the `Authorization: Bearer` header on every request.
</Note>

## List contacts

`GET /api/v1/contacts/contact`

Returns a paginated list of contacts for the authenticated tenant. Filter by company or run a full-text search across names and emails.

### Query parameters

<ParamField query="companyId" type="string">
  Filter contacts belonging to a specific company. Must be a valid UUID.
</ParamField>

<ParamField query="search" type="string">
  Full-text search across contact names and email addresses.
</ParamField>

<ParamField query="page" type="integer" default="1">
  1-based page index.
</ParamField>

<ParamField query="limit" type="integer" default="25">
  Number of results per page. Maximum `100`.
</ParamField>

### Response

```json theme={null}
{
  "data": [
    {
      "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
      "companyId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@acme.com",
      "phone": "+15550001234",
      "role": "Procurement Manager",
      "isPrimary": true,
      "notes": null,
      "customFields": {},
      "createdAt": "2024-03-15T10:30:00Z",
      "updatedAt": "2024-03-15T10:30:00Z"
    }
  ],
  "total": 84,
  "page": 1,
  "pageSize": 25
}
```

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://app.novala.ai/api/v1/contacts/contact?companyId=d290f1ee-6c54-4b01-90e6-d701748f0851" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://app.novala.ai/api/v1/contacts/contact?companyId=d290f1ee-6c54-4b01-90e6-d701748f0851',
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const { data, total } = await response.json();
  ```
</CodeGroup>

***

## Create a contact

`POST /api/v1/contacts/contact`

Creates a new contact linked to an existing company.

### Request body

<ParamField body="companyId" type="string" required>
  UUID of the company this contact belongs to.
</ParamField>

<ParamField body="firstName" type="string" required>
  Contact's first name. Maximum 100 characters.
</ParamField>

<ParamField body="lastName" type="string" required>
  Contact's last name. Maximum 100 characters.
</ParamField>

<ParamField body="email" type="string">
  Contact's email address. Must be a valid email format.
</ParamField>

<ParamField body="phone" type="string">
  Contact's phone number. Maximum 50 characters.
</ParamField>

<ParamField body="role" type="string">
  Job title or role within the company. Maximum 100 characters.
</ParamField>

<ParamField body="isPrimary" type="boolean">
  When `true`, marks this as the primary contact for the company.
</ParamField>

<ParamField body="notes" type="string">
  Internal notes about this contact.
</ParamField>

<ParamField body="customFields" type="object">
  Key-value map of custom field data. Any required custom fields your tenant has configured must be included.
</ParamField>

### Response

Returns `201 Created` with the new contact wrapped in `{ "data": {...} }`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.novala.ai/api/v1/contacts/contact \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "companyId": "d290f1ee-6c54-4b01-90e6-d701748f0851",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane.doe@acme.com",
      "role": "Procurement Manager",
      "isPrimary": true
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://app.novala.ai/api/v1/contacts/contact', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      companyId: 'd290f1ee-6c54-4b01-90e6-d701748f0851',
      firstName: 'Jane',
      lastName: 'Doe',
      email: 'jane.doe@acme.com',
      role: 'Procurement Manager',
      isPrimary: true,
    }),
  });
  const { data } = await response.json(); // 201 Created
  ```
</CodeGroup>

***

## Get a contact

`GET /api/v1/contacts/contact/{id}`

Retrieves a single contact by ID.

### Path parameters

<ParamField path="id" type="string" required>
  UUID of the contact.
</ParamField>

### Response

Returns `200 OK` with `{ "data": { contact object } }`. Returns `404 Not Found` if the contact does not exist or belongs to a different tenant.

### Response fields

<ResponseField name="data" type="object">
  <Expandable title="Contact object">
    <ResponseField name="id" type="string">UUID of the contact.</ResponseField>
    <ResponseField name="companyId" type="string">UUID of the associated company.</ResponseField>
    <ResponseField name="firstName" type="string">First name.</ResponseField>
    <ResponseField name="lastName" type="string">Last name.</ResponseField>
    <ResponseField name="email" type="string | null">Email address.</ResponseField>
    <ResponseField name="phone" type="string | null">Phone number.</ResponseField>
    <ResponseField name="role" type="string | null">Role or job title at the company.</ResponseField>
    <ResponseField name="isPrimary" type="boolean">Whether this is the primary contact for the company.</ResponseField>
    <ResponseField name="notes" type="string | null">Internal notes.</ResponseField>
    <ResponseField name="customFields" type="object">Custom field key-value pairs.</ResponseField>
    <ResponseField name="createdAt" type="string">ISO 8601 creation timestamp.</ResponseField>
    <ResponseField name="updatedAt" type="string">ISO 8601 last-updated timestamp.</ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET https://app.novala.ai/api/v1/contacts/contact/3f2504e0-4f89-11d3-9a0c-0305e82c3301 \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://app.novala.ai/api/v1/contacts/contact/3f2504e0-4f89-11d3-9a0c-0305e82c3301',
    { headers: { 'Authorization': `Bearer ${apiKey}` } }
  );
  const { data } = await response.json();
  ```
</CodeGroup>

***

## Update a contact

`PATCH /api/v1/contacts/contact/{id}`

Partially updates a contact. Only include the fields you want to change. Pass `null` to clear a nullable field.

### Path parameters

<ParamField path="id" type="string" required>
  UUID of the contact to update.
</ParamField>

### Request body

All fields are optional.

<ParamField body="firstName" type="string">
  Updated first name. Maximum 100 characters.
</ParamField>

<ParamField body="lastName" type="string">
  Updated last name. Maximum 100 characters.
</ParamField>

<ParamField body="email" type="string | null">
  Updated email address. Pass `null` to clear.
</ParamField>

<ParamField body="phone" type="string | null">
  Updated phone number. Pass `null` to clear.
</ParamField>

<ParamField body="role" type="string | null">
  Updated role. Pass `null` to clear.
</ParamField>

<ParamField body="isPrimary" type="boolean">
  Whether to mark this contact as the primary contact.
</ParamField>

<ParamField body="notes" type="string | null">
  Updated internal notes. Pass `null` to clear.
</ParamField>

<ParamField body="customFields" type="object">
  Replaces all custom field values with the provided map.
</ParamField>

### Response

Returns `200 OK` with `{ "data": { updated contact } }`. Returns `404 Not Found` if the contact does not exist.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://app.novala.ai/api/v1/contacts/contact/3f2504e0-4f89-11d3-9a0c-0305e82c3301 \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "role": "VP of Operations", "isPrimary": true }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    'https://app.novala.ai/api/v1/contacts/contact/3f2504e0-4f89-11d3-9a0c-0305e82c3301',
    {
      method: 'PATCH',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ role: 'VP of Operations', isPrimary: true }),
    }
  );
  const { data } = await response.json();
  ```
</CodeGroup>
