Skip to main content
POST
Define a contact field (public API)
Defines a new field your contacts can carry — and your agents can speak.

Overview

Creating a field is a schema change, not a data change: from this point on the key is offered in the agent designer’s variable palette, accepted by PATCH /contacts/{id}, and available as a CSV import target. Anyone who can write your contacts can define one.

Choosing a key

The key becomes the prompt token {{contact.<key>}}, so it has to be something the token grammar can express: lowercase letters, digits and underscores, starting with a letter, up to 80 characters. order_number is fine; Order-Number, 2nd_order and order number are each a 400.
A key cannot be changed later. It is the identity of every {{contact.<key>}} token already saved inside an agent’s flow and of every value already stored on a contact, and those cannot be migrated in one step — a rename would blank the tokens and orphan the values. PATCH has no key property at all. Choose the key as deliberately as a column name.
Some keys are reserved, because the platform resolves them itself on every call: first_name, last_name, email, phone, company_name, company_description, agent_name and profile. Attempting one is a 400 naming it, rather than a field that is created and then never spoken. Every other seeded key is yours to shadow. order_number, job_title and lead_source are platform fields, but creating a company field with the same key is legal and meaningful — it overrides the label and type for your company alone, and the response comes back with isCompanyOverride: true. So isSystem: true does not mean “cannot be used”: it means the platform seeded it, and only the eight keys above are actually refused.
This is the intended path for the three enum-shaped seeded fields. company_size, industry and lead_status are seeded as string, because one company’s lead statuses are not another’s and platform options would be wrong for nearly everybody. Shadow the key with an enum carrying your own enumOptions and you get the constraint without losing the CSV aliases the seeded field brought with it. The category stays the seeded one, so the field does not move panels in the dashboard.

Choosing a type

tags is for an open-ended set of labels — segments, flags, anything a person adds to as they go. Reach for enum instead when the set is closed and you want a value outside it refused. tags is also the one type that takes two shapes on the way in, an array or a comma-separated string, so a single CSV cell holding several tags maps onto one field. A comma is always a separator, so a tag cannot contain one — that holds inside an array element too. A value that trims down to nothing clears the field, the way "" does for text. Bounded three ways, and the third is the one that catches people: at most 50 tags, 60 characters per tag, and 2000 characters of tag text per request — so the first two are not independent, and fifty sixty-character tags is over the aggregate. Each limit is a 422 naming it. See Set Contact Fields for the full write contract. enum requires enumOptions, and the options must be unique. An enum with no options would accept anything while claiming to be a closed set, and an import mapped against it would pass preview and then fail row by row — so it is refused at creation instead. Coercion is deliberately narrow. A CSV and a JSON body disagree about types for the same field, so a string that unambiguously denotes the declared type is accepted and nothing else is guessed: "twelve" is not a number, and "maybe" is not a boolean.

Marking personal data

Set isPii: true for anything that identifies a person. The value is then masked in logs, and it is excluded from what reaches an agent unless you also set allowPiiInContext. It is not forbidden, though. Setting both is a deliberate act with a real use: for a collections agent the amount owed is the entire reason for the call, and an agent that cannot name it is worse than one that can. What you are choosing when you set both is to put that value in the prompt of every call for a contact who has one — so choose it per field, and leave it off otherwise.

Limits

A company may define at most 50 fields of its own. Seeded fields do not count against it.

Scopes

write:contacts (or its write:candidates alias).
A field with allowPiiInContext is summarised into the prompt of every call for a contact that has a value for it, so defining fields does shape what your agents say. It is not behind a scope of its own: the keys that can write your contacts are your own and your ATS partner’s, and a separate grant would be one more thing to discover without narrowing who actually holds it.

Error Scenarios

  • 400 Bad Request: a malformed key; a key you have already defined; a key the platform reserves; an enum with no options or with duplicates; more than 50 fields
  • 403 Forbidden: the key lacks write:contacts

List Contact Fields

See the catalog, seeded fields included

Update Contact Field

Change everything except the key

Update Contact

Store values against a contact

Delete Contact Field

Remove a definition, keep the values

Authorizations

Authorization
string
header
required

API key for authentication using Bearer scheme

Query Parameters

companyId
string

Required for ATS API keys to specify which company to access. Ignored for standard company API keys.

Body

application/json
key
string
required

The key you will address this field by. Lowercase letters, digits and underscores, starting with a letter. It becomes {{contact.<key>}} in a prompt and the key in a values payload — and it cannot be changed afterwards.

Maximum string length: 80
Example:

"order_number"

label
string
required

Human label, shown wherever the field is offered for editing

Example:

"Order number"

dataType
enum<string>
required

The value type. Writes to this field are validated against it. tags stores a string[] and accepts either an array or a comma-separated string, so one CSV cell maps onto one field; a value that trims down to no tags is read as clearing the field.

Available options:
string,
number,
date,
bool,
enum,
tags
Example:

"string"

enumOptions
string[]

The allowed values. Required when dataType is ENUM — an enum with no options would accept anything while claiming to be a closed set — and ignored for every other type.

Example:
isPii
boolean
default:false

Mark the field as personal data. 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).

csvAliases
string[]

Extra CSV header names this field should also match during an import Stored for the contact-field importer, which has not shipped: setting this changes no import behaviour today.

Example:
allowPiiInContext
boolean
default:false

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

description
string

What the field means

Maximum string length: 300
displayOrder
number
default:0

Ascending display order; ties break on key

Response

id
string<uuid>
required

Field id. Needed only to address PATCH and DELETE; every other surface uses key.

Example:

"123e4567-e89b-12d3-a456-426614174000"

key
string
required

The key you address this field by, in {{contact.<key>}} and in a values payload

Example:

"order_number"

label
string
required

Human label, shown wherever the field is offered for editing

Example:

"Order number"

dataType
enum<string>
required

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.

Available options:
string,
number,
date,
bool,
enum,
tags
Example:

"string"

category
enum<string>
required

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.

Available options:
identity,
organisation,
commercial,
relationship,
scheduling,
recruiting,
platform,
custom
Example:

"commercial"

isPii
boolean
required

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
boolean
required

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

allowPiiInContext
boolean
required

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
boolean
required

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

displayOrder
number
required

Ascending display order; ties break on key

Example:

50

isCompanyOverride
boolean
required

True when this is your own field overriding a seeded field of the same key

Example:

false

readOnly
boolean
required

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
boolean
required

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

enumOptions
string[]

The allowed values, for an ENUM field. Absent for every other type.

Example:
csvAliases
string[]

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:
description
string

What the field means

Example:

"The order this call is about"