curl --request POST \
--url https://api.instaview.sk/contact-fields \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "order_number",
"label": "Order number",
"dataType": "string",
"enumOptions": [
"pricing page",
"webinar",
"referral"
],
"isPii": false,
"csvAliases": [
"Order #",
"OrderNo"
],
"allowPiiInContext": false,
"description": "<string>",
"displayOrder": 0
}
'import requests
url = "https://api.instaview.sk/contact-fields"
payload = {
"key": "order_number",
"label": "Order number",
"dataType": "string",
"enumOptions": ["pricing page", "webinar", "referral"],
"isPii": False,
"csvAliases": ["Order #", "OrderNo"],
"allowPiiInContext": False,
"description": "<string>",
"displayOrder": 0
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
key: 'order_number',
label: 'Order number',
dataType: 'string',
enumOptions: ['pricing page', 'webinar', 'referral'],
isPii: false,
csvAliases: ['Order #', 'OrderNo'],
allowPiiInContext: false,
description: '<string>',
displayOrder: 0
})
};
fetch('https://api.instaview.sk/contact-fields', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.instaview.sk/contact-fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'key' => 'order_number',
'label' => 'Order number',
'dataType' => 'string',
'enumOptions' => [
'pricing page',
'webinar',
'referral'
],
'isPii' => false,
'csvAliases' => [
'Order #',
'OrderNo'
],
'allowPiiInContext' => false,
'description' => '<string>',
'displayOrder' => 0
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.instaview.sk/contact-fields"
payload := strings.NewReader("{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.instaview.sk/contact-fields")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/contact-fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}"
response = http.request(request)
puts response.read_body{
"id": "123e4567-e89b-12d3-a456-426614174000",
"key": "order_number",
"label": "Order number",
"dataType": "string",
"category": "commercial",
"isPii": false,
"isSystem": false,
"allowPiiInContext": false,
"inEssentialPromptSet": false,
"displayOrder": 50,
"isCompanyOverride": false,
"readOnly": false,
"writable": true,
"enumOptions": [
"pricing page",
"webinar",
"referral"
],
"csvAliases": [
"Order #",
"OrderNo"
],
"description": "The order this call is about"
}Create Contact Field
Creates a field for your company. A key matching a seeded field shadows it for you alone; a key the platform reserves for itself is refused. A company may define at most 50 fields.
curl --request POST \
--url https://api.instaview.sk/contact-fields \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"key": "order_number",
"label": "Order number",
"dataType": "string",
"enumOptions": [
"pricing page",
"webinar",
"referral"
],
"isPii": false,
"csvAliases": [
"Order #",
"OrderNo"
],
"allowPiiInContext": false,
"description": "<string>",
"displayOrder": 0
}
'import requests
url = "https://api.instaview.sk/contact-fields"
payload = {
"key": "order_number",
"label": "Order number",
"dataType": "string",
"enumOptions": ["pricing page", "webinar", "referral"],
"isPii": False,
"csvAliases": ["Order #", "OrderNo"],
"allowPiiInContext": False,
"description": "<string>",
"displayOrder": 0
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
key: 'order_number',
label: 'Order number',
dataType: 'string',
enumOptions: ['pricing page', 'webinar', 'referral'],
isPii: false,
csvAliases: ['Order #', 'OrderNo'],
allowPiiInContext: false,
description: '<string>',
displayOrder: 0
})
};
fetch('https://api.instaview.sk/contact-fields', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.instaview.sk/contact-fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'key' => 'order_number',
'label' => 'Order number',
'dataType' => 'string',
'enumOptions' => [
'pricing page',
'webinar',
'referral'
],
'isPii' => false,
'csvAliases' => [
'Order #',
'OrderNo'
],
'allowPiiInContext' => false,
'description' => '<string>',
'displayOrder' => 0
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.instaview.sk/contact-fields"
payload := strings.NewReader("{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.instaview.sk/contact-fields")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/contact-fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"key\": \"order_number\",\n \"label\": \"Order number\",\n \"dataType\": \"string\",\n \"enumOptions\": [\n \"pricing page\",\n \"webinar\",\n \"referral\"\n ],\n \"isPii\": false,\n \"csvAliases\": [\n \"Order #\",\n \"OrderNo\"\n ],\n \"allowPiiInContext\": false,\n \"description\": \"<string>\",\n \"displayOrder\": 0\n}"
response = http.request(request)
puts response.read_body{
"id": "123e4567-e89b-12d3-a456-426614174000",
"key": "order_number",
"label": "Order number",
"dataType": "string",
"category": "commercial",
"isPii": false,
"isSystem": false,
"allowPiiInContext": false,
"inEssentialPromptSet": false,
"displayOrder": 50,
"isCompanyOverride": false,
"readOnly": false,
"writable": true,
"enumOptions": [
"pricing page",
"webinar",
"referral"
],
"csvAliases": [
"Order #",
"OrderNo"
],
"description": "The order this call is about"
}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 byPATCH /contacts/{id}, and available
as a CSV import target. Anyone who can write your contacts can define one.
POST /contact-fields
{
"key": "order_number",
"label": "Order number",
"dataType": "string",
"description": "The order this call is about",
"csvAliases": ["Order #", "OrderNo"],
"allowPiiInContext": false,
"displayOrder": 50
}
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.
{{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.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.
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
dataType | Accepts | Stored as |
|---|---|---|
string | any text | trimmed text |
number | 42, "42", "-7.5" | a number |
bool | true, "true", "yes", "1" (and their negatives) | a boolean |
date | anything Date can parse, e.g. "2026-04-20" | an ISO 8601 timestamp |
enum | one of your own enumOptions | the matching option |
tags | ["vip","renewal"], or "vip, renewal" | a string[], trimmed and de-duplicated |
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
SetisPii: 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).
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
enumwith no options or with duplicates; more than 50 fields - 403 Forbidden: the key lacks
write:contacts
Related Resources
List Contact Fields
Update Contact Field
Update Contact
Delete Contact Field
Authorizations
API key for authentication using Bearer scheme
Query Parameters
Required for ATS API keys to specify which company to access. Ignored for standard company API keys.
Body
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.
80"order_number"
Human label, shown wherever the field is offered for editing
"Order number"
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.
string, number, date, bool, enum, tags "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.
["pricing page", "webinar", "referral"]
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).
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.
["Order #", "OrderNo"]
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.
false
What the field means
300Ascending display order; ties break on key
Response
Field id. Needed only to address PATCH and DELETE; every other surface uses key.
"123e4567-e89b-12d3-a456-426614174000"
The key you address this field by, in {{contact.<key>}} and in a values payload
"order_number"
Human label, shown wherever the field is offered for editing
"Order number"
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.
string, number, date, bool, enum, tags "string"
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.
identity, organisation, commercial, relationship, scheduling, recruiting, platform, custom "commercial"
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).
false
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.
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.
false
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.
false
Ascending display order; ties break on key
50
True when this is your own field overriding a seeded field of the same key
false
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.
false
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".
true
The allowed values, for an ENUM field. Absent for every other type.
["pricing page", "webinar", "referral"]
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.
["Order #", "OrderNo"]
What the field means
"The order this call is about"