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

# List Contact Fields

> Every field your contacts can carry: the platform's seeded fields merged with your own, which shadow a seeded field of the same key. Read this to learn which keys `fields` accepts on `POST /contacts` and `PATCH /contacts/{id}`, and what each one expects.

Lists every field your contacts can carry, and what each one expects.

## Overview

A contact field is a typed, named slot on a contact — `order_number`, `lead_source`,
`renewal_date`. Two things make a field different from a note you keep on your own side:

1. **An agent can speak it.** `{{contact.order_number}}` in an agent's prompt is replaced with
   this contact's value at the moment the call is placed.
2. **The platform validates it.** A field declared `number` rejects `"twelve"` on the way in,
   rather than surfacing as a confused agent mid-call.

Read this endpoint first. It tells you which keys
[`PATCH /contacts/{id}`](/api-reference/contacts/update-contact) will accept
and how each value must be shaped.

<Warning>
  **The catalog is thirty-six fields, not eleven.** InstaView seeds a default set covering
  identity, the contact's employer, orders and invoices, the relationship, appointments and
  screening — so most of what you would have defined by hand already exists, with CSV aliases
  attached. If you built a form or a table that renders every field it gets back, expect
  thirty-six rows rather than a handful, and read `category` if you want them grouped the way
  the dashboard groups them.
</Warning>

## What you get back

Two kinds of field, merged into one list:

|                   | Where it comes from                   | Can you change it?            |
| ----------------- | ------------------------------------- | ----------------------------- |
| `isSystem: true`  | Seeded by InstaView for every company | No — but most can be shadowed |
| `isSystem: false` | Defined by you                        | Yes                           |

A seeded field is the platform's contract with the prompt compiler and the CSV importer, so
one company editing it directly would change what its token means for everybody. To change a
seeded field's label or type **for your company alone**, define your own field with the same
key: it shadows the seeded one, and comes back with `isCompanyOverride: true`.

<Warning>
  **Eight keys are RESERVED and cannot be shadowed at all**: `first_name`, `last_name`,
  `email`, `phone`, `company_name`, `company_description`, `agent_name` and `profile`. A
  `POST /contact-fields` on one of those is refused — the platform resolves them itself, so a
  company definition would be advertised in the palette and then ignored on the call.

  `isSystem: true` alone does not tell you which is which, because most seeded keys
  (`order_number`, `is_vip`, `lead_source`) *can* be shadowed. Check `writable` if what you
  need to know is whether you can send a value for a key.
</Warning>

```javascript theme={null}
GET /contact-fields
```

```json theme={null}
{
  "data": [
    {
      "id": "3f1b...",
      "key": "first_name",
      "label": "First name",
      "dataType": "string",
      "category": "identity",
      "isPii": true,
      "isSystem": true,
      "displayOrder": 10,
      "isCompanyOverride": false,
      "readOnly": false,
      "writable": false
    },
    {
      "id": "9c22...",
      "key": "renewal_date",
      "label": "Renewal date",
      "dataType": "date",
      "category": "commercial",
      "csvAliases": ["renewal", "renewal date", "renews", "dátum obnovenia"],
      "isPii": false,
      "isSystem": true,
      "displayOrder": 50,
      "isCompanyOverride": false,
      "readOnly": false,
      "writable": true
    }
  ],
  "total": 36
}
```

## Reading the flags

<AccordionGroup>
  <Accordion title="dataType — how a value is validated">
    One of `string`, `number`, `date`, `bool`, `enum` or `tags`. A write is checked against it: a
    `number` field takes `42` or `"42"` but not `"twelve"`; a `bool` field takes `true`,
    `"true"`, `"yes"` or `"1"` and their negatives, but not `"maybe"`; a `date` field is stored
    as an ISO timestamp; an `enum` field takes only a value from its own `enumOptions`.

    A `tags` field stores a `string[]` and is the one type that accepts two shapes: an array,
    **or** a comma-separated string, so one CSV cell holding several tags maps onto one field.
    See [Set Contact Fields](/api-reference/contacts/update-contact#tags-takes-two-shapes)
    for what it trims, de-duplicates and treats as an erase.
  </Accordion>

  <Accordion title="category — which panel the field belongs to">
    One of `identity`, `organisation`, `commercial`, `relationship`, `scheduling`,
    `recruiting`, `platform` or `custom`. It is the grouping the dashboard's variable palette
    uses, and the first key this list is ordered by — so iterating the response and breaking on
    a change of `category` gives you the same panels a user sees.

    Set by InstaView and not writable. A field you define is always `custom`; a field of yours
    that shadows a seeded one reports the **seeded** field's category, so `job_title` stays
    under `organisation` for everybody even after you relabel it.
  </Accordion>

  <Accordion title="isPii — personal data, and what it keeps out of a call">
    A PII field is masked in logs and is **excluded** from everything an agent gets: it is
    neither in the prompt nor in the document the agent can query. That is what keeps `email`
    and `phone` off every call without anyone deciding, and it is enforced rather than
    defaulted.

    Two things get past it. `inEssentialPromptSet` below, which is ours — `first_name` is
    personal data and is also the first word of the call. And `allowPiiInContext`, which is
    yours: set it on the one field an agent has to be able to say, and the value joins both the
    document and the prompt. For a collections agent the amount owed is the entire reason for
    the call. Setting it on a field that is not `isPii` is a `400`, not a no-op.
  </Accordion>

  <Accordion title="allowPiiInContext — let one personal field through">
    Your decision that this particular personal datum belongs on a call. Writable, and only
    meaningful on an `isPii` field. See
    [Create Contact Field](/api-reference/contact-fields/create-contact-field).
  </Accordion>

  <Accordion title="inEssentialPromptSet — in the prompt, not just queryable">
    Read-only, and ours to decide. A short set of fields goes into the prompt of every
    context-enabled agent, because a prompt line is always known while a document entry is known
    only if the agent thinks to look. Everything else a contact has a value for reaches the
    agent through the generated contact document instead, which costs nothing until it is
    queried.

    Both halves are gated by
    [`contextConfig.useContactContext`](/api-reference/agents/create-agent#telling-the-agent-about-the-person-it-is-calling)
    on the agent. There is no per-field opt-in to maintain: define fields, store values, and an
    agent with the switch on can use them.
  </Accordion>

  <Accordion title="readOnly — catalogued but not storable">
    A handful of fields exist so an author can use the token, but resolve from somewhere other
    than the contact: `company_description` is your company's own blurb, `agent_name` is the
    agent's. Sending a value for one is a `422` naming the key. They are listed here because an agent author
    still needs to see them.
  </Accordion>

  <Accordion title="writable — whether you can send a value">
    **The flag to branch on before a write**, and not the same thing as `!readOnly`. Look at
    `first_name` in the response above: `readOnly` is `false`, and a value for it is still
    refused with a `422`. It is one of the keys the platform RESERVES — the identity fields
    (`first_name`, `last_name`, `email`, `phone`) plus `company_name`, `company_description`,
    `agent_name` and `profile`. Those four identity fields *are* writable, just as properties of
    [the contact itself](/api-reference/contacts/update-contact) rather than as fields.

    So `readOnly` answers "where does this value come from" and `writable` answers "will this
    endpoint take one from me". `writable: false` always means do not send a value for this key;
    it covers every `readOnly` field as well as every reserved one.
  </Accordion>

  <Accordion title="csvAliases — extra header names for imports">
    Header names an import should also match this field on, beyond the key and label — so a
    sheet whose column is called `Order #` can be recognised as `order_number` rather than
    mapped by hand.

    Every writable seeded field arrives with aliases in English, Slovak and Czech —
    `renewal_date` carries `renewal`, `renews`, `dátum obnovenia` and more — ready for the
    importer to match on. Adding your own with
    [`PATCH /contact-fields/{id}`](/api-reference/contact-fields/update-contact-field) does not
    touch InstaView's. Read the warning below first: nothing consumes any of them yet.

    <Warning>
      **Declared, but nothing consumes it yet.** The current CSV importer maps against a fixed
      list of built-in contact properties and does not read this array, so setting
      `csvAliases` today changes no import behaviour. It is stored so the catalog is complete
      before the contact-field import lands. Until then, map these columns explicitly.
    </Warning>
  </Accordion>

  <Accordion title="id vs key">
    `id` exists only to address `PATCH` and `DELETE`. Everywhere else — a prompt token, a
    values payload, a CSV mapping — a field is addressed by `key`.
  </Accordion>
</AccordionGroup>

## Ordering and paging

Ordered by `category` first — in the fixed order `identity`, `organisation`, `commercial`,
`relationship`, `scheduling`, `recruiting`, `platform`, `custom` — then by `displayOrder`, then
by `key`. `displayOrder` therefore only ever separates fields **within** one category, which is
why each category's own numbering restarts rather than continuing one long sequence.

Unpaginated: your whole catalog is smaller than one page of any other list endpoint, so there
is nothing to page through.

## Scopes

`read:contacts` (or its `read:candidates` alias). Changing the catalog needs `write:contacts`.
Nothing to rotate: any key that can already read your contacts can read their field catalog.

## Related Resources

<CardGroup cols={2}>
  <Card title="Create Contact Field" icon="plus" href="/api-reference/contact-fields/create-contact-field">
    Define a field of your own
  </Card>

  <Card title="Update Contact" icon="pen" href="/api-reference/contacts/update-contact">
    Store values against a contact
  </Card>

  <Card title="Get Contact" icon="eye" href="/api-reference/contacts/get-contact">
    Read one contact's values
  </Card>

  <Card title="Update Contact Field" icon="pen" href="/api-reference/contact-fields/update-contact-field">
    Change one of your own fields
  </Card>
</CardGroup>


## OpenAPI

````yaml GET /contact-fields
openapi: 3.0.0
info:
  title: InstaView API
  description: |-
    InstaView API Documentation

    ## Authentication

    All endpoints require API key authentication using Bearer token:
    ```
    Authorization: Bearer sk_your_api_key_here
    ```

    ## API Key Management

    The API Key module provides comprehensive key management for:
    - **Direct Client Keys**: Company-scoped keys for your applications
    - **ATS Partner Keys**: Resource-scoped keys for ATS integrations

    ### Key Features
    - HMAC-SHA256 hashing for API key storage
    - Configurable rate limiting
    - Comprehensive audit logging
    - Company-level isolation
    - Resource scoping for ATS partners
  version: 1.0.0
  contact: {}
servers:
  - url: https://api.instaview.sk
    description: Production API Gateway
security: []
tags: []
paths:
  /contact-fields:
    get:
      tags:
        - Public Contact Fields
      summary: List contact fields (public API)
      description: >-
        Every field your contacts can carry: the platform's seeded fields merged
        with your own, which shadow a seeded field of the same key. Read this to
        learn which keys `fields` accepts on `POST /contacts` and `PATCH
        /contacts/{id}`, and what each one expects.
      operationId: PublicContactFieldsController_listContactFields_v1
      parameters:
        - name: companyId
          required: false
          in: query
          description: >-
            Required for ATS API keys to specify which company to access.
            Ignored for standard company API keys.
          schema:
            type: string
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicListContactFieldsResponseDto'
      security:
        - bearer: []
        - bearer: []
components:
  schemas:
    PublicListContactFieldsResponseDto:
      type: object
      properties:
        data:
          description: Every field this company can use, ordered by displayOrder then key
          type: array
          items:
            $ref: '#/components/schemas/PublicContactFieldDto'
        total:
          type: number
          description: Number of fields in the catalog
          example: 12
      required:
        - data
        - total
    PublicContactFieldDto:
      type: object
      properties:
        id:
          type: string
          description: >-
            Field id. Needed only to address PATCH and DELETE; every other
            surface uses `key`.
          format: uuid
          example: 123e4567-e89b-12d3-a456-426614174000
        key:
          type: string
          description: >-
            The key you address this field by, in `{{contact.<key>}}` and in a
            values payload
          example: order_number
        label:
          type: string
          description: Human label, shown wherever the field is offered for editing
          example: Order number
        dataType:
          type: string
          description: >-
            The value type this field accepts. Writes are validated against it.
            `tags` is a `string[]` and accepts either an array or a
            comma-separated string, so one CSV cell maps onto one field.
          enum:
            - string
            - number
            - date
            - bool
            - enum
            - tags
          example: string
        category:
          type: string
          description: >-
            Which panel of the catalog this field belongs to — the grouping the
            dashboard's variable palette uses, and the first key this list is
            ordered by. Set by InstaView and not writable: a field you define is
            always `custom`, and a field of yours that shadows a seeded one
            reports the seeded field's category so the grouping is the same for
            every company.
          enum:
            - identity
            - organisation
            - commercial
            - relationship
            - scheduling
            - recruiting
            - platform
            - custom
          example: commercial
        enumOptions:
          description: The allowed values, for an ENUM field. Absent for every other type.
          example:
            - pricing page
            - webinar
            - referral
          type: array
          items:
            type: string
        isPii:
          type: boolean
          description: >-
            Personal data. Masked in logs, and EXCLUDED from everything that
            reaches the agent — the prompt and the generated contact document
            alike — unless `allowPiiInContext` is set on this field, or the
            platform lists it in the essential prompt set
            (`inEssentialPromptSet`).
          example: false
        isSystem:
          type: boolean
          description: >-
            A field the platform defines and every company shares. A seeded
            field cannot be edited or deleted directly; for most of them you can
            define your own field with the same key to override the label and
            type for your company alone. Eight keys are RESERVED and refuse even
            that (`first_name`, `last_name`, `email`, `phone`, `company_name`,
            `company_description`, `agent_name`, `profile`), because the
            platform resolves them itself.
          example: false
        csvAliases:
          description: >-
            Extra CSV header names this field also matches during an import
            Stored for the contact-field importer, which has not shipped:
            setting this changes no import behaviour today.
          example:
            - 'Order #'
            - OrderNo
          type: array
          items:
            type: string
        allowPiiInContext:
          type: boolean
          description: >-
            Let this one personal field reach the agent anyway. It joins the
            generated contact document AND the prompt, because a queryable value
            is one the agent might never think to look up, and reliability is
            the point for a field like an outstanding balance. Only meaningful
            when `isPii` is true; sending it on a field that is not personal
            data is rejected rather than ignored.
          example: false
        inEssentialPromptSet:
          type: boolean
          description: >-
            Read-only. Whether the platform puts this field in every
            context-enabled agent's prompt: a short set covering what an agent
            needs to open a call correctly. Not settable — everything else the
            contact has reaches the agent through the generated contact document
            instead, which is queried mid-call rather than paid for on every
            dial.
          example: false
        description:
          type: string
          description: What the field means
          example: The order this call is about
        displayOrder:
          type: number
          description: Ascending display order; ties break on key
          example: 50
        isCompanyOverride:
          type: boolean
          description: >-
            True when this is your own field overriding a seeded field of the
            same key
          example: false
        readOnly:
          type: boolean
          description: >-
            Catalogued so an agent author can use the token, but resolved by the
            platform on every call rather than stored per contact
            (`company_description` is your company's own blurb, not the
            contact's data). Sending a value for one is a 422. Note this is not
            the whole of what you can write — check `writable` for that.
          example: false
        writable:
          type: boolean
          description: >-
            Whether `fields` on `POST /contacts` or `PATCH /contacts/{id}`
            accepts a value for this key. The one flag to branch on before
            writing: `readOnly` alone is not enough, because a key the platform
            RESERVES (`email`, `first_name`, `phone` and the rest of the
            identity fields) reports `readOnly: false` and is still refused with
            a 422 — those are writable, but through the contact resource's own
            properties rather than as a field. False also for every read-only
            key, so a false here always means "do not send a value for this".
          example: true
      required:
        - id
        - key
        - label
        - dataType
        - category
        - isPii
        - isSystem
        - allowPiiInContext
        - inEssentialPromptSet
        - displayOrder
        - isCompanyOverride
        - readOnly
        - writable
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      description: API key for authentication using Bearer scheme

````