Skip to main content
POST
Create contact
POST /candidates is a permanent alias of this endpoint and keeps working unchanged, with the same scopes and the same response. See Resource names.
Creates a contact — a person an agent can call. A contact can optionally be associated with a job, or exist on its own.

Overview

A contact is the person on the other end of a conversation: the record holding their name, how to reach them, and whatever else you want to keep about them. Jobs are optional and can be attached at creation or later.

Use Cases

  • Application processing: create contacts from inbound applications, with a job attached
  • ATS integration: sync contacts in from an external system
  • Manual entry: add people you found some other way
  • Building a list: keep contacts with no job assignment at all
  • Bulk import: load many contacts at once

Job Association (Optional)

A job is optional. Send jobId to attach one at creation, or leave it out.
  • With a job: the contact is associated with that job immediately.
  • Without one: the contact exists on its own, and jobs can be attached later through the update endpoint. Most callers outside hiring never send one.
  • If provided, the job must exist and belong to your API key’s company.

Basic Contact Creation

With Job Association

Without Job Association

A Fuller Contact

Note: resumeUrl and linkedinUrl are not built-in fields — they are shown inside the free-form metadata object, which is where anything of your own belongs.
metadata is not readable by an agent. It is your own scratch space — free-form, unvalidated, and never placed in a prompt. If you want a value an agent can say, it has to be a contact field: catalogued, typed, and addressable as {{contact.<key>}}. See Update Contact.
workHistory describes previous employment, and its candidatePosition field keeps that name because it is the name the API accepts. It exists for reference calls: each entry can carry a referenceName and referencePhone to call about that job.

Company Scoping

Contacts are scoped to your API key’s company. When a job is attached, the job must belong to that company; a contact created without one is associated with your company anyway, and can only be reached through your company’s API keys. That is what keeps companies fully isolated from each other.

Required Fields

Required on every contact:
  • firstName — 1-100 characters
  • lastName — 1-100 characters
  • gdprExpiryDate — GDPR retention date, ISO 8601, and in the future (e.g. "2026-11-16")
  • At least one way to reach them: email or phoneNumber (or both)

Optional Fields

  • jobId: UUID of a job to attach (must exist and belong to your company)
  • email: valid email address (required if phoneNumber is absent)
  • phoneNumber: E.164 format, e.g. +1234567890 (required if email is absent)
  • gender: "male" or "female". Used for gender-aware addressing on the call. Auto-detected from the name when omitted.
  • metadata: your own key-value pairs (max 10KB, 5 levels deep, 50 keys)
  • fields: catalogued field values, keyed by catalog key — validated against the catalog, and the only values an agent can speak

Field Validation

The API enforces:
  • firstName/lastName: 1-100 characters (required)
  • gdprExpiryDate: a valid ISO 8601 date in the future (required)
  • email: valid email format (at least one of email or phoneNumber required)
  • phoneNumber: starts with +, E.164 format (at least one of email or phoneNumber required)
  • jobId: a valid UUID naming an existing job in your company (optional)
  • gender: "male" or "female" if present (optional)
  • metadata: max 10KB, max depth 5, max 50 keys (optional)

Setting Field Values on the Create

Send them as fields, keyed by catalog key, and the contact is created with its values in one request:
The response’s fields is what was stored, so there is nothing to go and check. The shape is flat — the same one GET /contacts/{id} returns and PATCH /contacts/{id} accepts. There is no seeded-versus-your-own split in the payload because there is none to make: every key this accepts is a catalogued key with writable: true, whether InstaView seeded it or you defined it. Read GET /contact-fields for the keys your company has.
A rejected value takes the contact with it. Values are validated against the catalog in the same transaction as the insert, so an unknown key or a wrong-typed value answers 422 naming it and the contact is not created. That is deliberate: a contact that exists without the values you sent is a half-write you cannot detect without re-reading.
An unknown key is a 422 by default. Pass ?createMissingFields=true to have it defined for you as a string field instead — convenient for a first sync, and opt-in per request because a typo would otherwise mint a permanent field in your catalog.

fields Is Not metadata

They are stored separately and read separately. fields is the validated door: every value is checked against its field’s declared type, enum and reserved-key rules, and only values that arrive through it are addressable as {{contact.<key>}}. metadata is your own scratch space, free-form and never read by an agent, and nothing you put in it becomes a contact field however you name the key.

Response Format

The response carries both jobId (singular) and jobIds (array):

Contacts Resource Guide

Learn about managing contacts and their workflows

Jobs Resource Guide

Understand job management

Conversations Resource Guide

Start a conversation with a contact

API Reference

List and manage your contacts

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.

createMissingFields
boolean
default:false

Define any key in fields that your catalog does not know yet, as a STRING field, instead of rejecting the request. Off by default, because a typo would otherwise become a permanent field in your catalog — visible in every agent's variable palette until somebody deletes it. A key the platform reserves, a malformed key, and a company already at its field ceiling are all still refused.

Body

application/json
firstName
string
required

The contact's first name

Required string length: 1 - 100
Example:

"John"

lastName
string
required

The contact's last name

Required string length: 1 - 100
Example:

"Doe"

gdprExpiryDate
string<date>
required

GDPR expiry date in ISO 8601 format (must be in the future)

Example:

"2026-11-16"

email
string

The contact's email address

Example:

"john.doe@example.com"

phoneNumber
string

The contact's phone number in E.164 format

Pattern: ^\+[1-9]\d{1,14}$
Example:

"+421915123456"

jobId
string<uuid>
deprecated

[Deprecated - use jobIds] Optional single job ID to associate.

Example:

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

jobIds
string<uuid>[]

Optional array of job IDs to associate the contact with. If both jobId and jobIds are provided, jobIds takes precedence.

Required array length: 1 - 50 elements
metadata
object

Custom metadata for extensibility (key-value pairs, max 10KB, 5 levels deep, 50 keys)

Example:
fields
object

Catalogued field values, keyed by catalog key — the same flat shape GET /contacts/{id} returns. Validated against each field's declared type, so an unknown key or a wrong-typed value is a 422 naming it, and the whole request — contact included — is rolled back. Read GET /contact-fields for the keys your company has. Not metadata: these are typed, and they are the only values an agent can speak.

Example:
gender
enum<string>

The contact's gender. Used for gender-aware addressing on the call. If not provided, it is auto-detected from the name.

Available options:
male,
female
Example:

"female"

cvText
string

The contact's CV in plain text. This will be automatically anonymized.

Maximum string length: 50000
Example:

"Jane Doe\nSoftware Engineer\nExperience: ..."

workHistory
object[]

Optional work history items for the contact.

Maximum array length: 20

Response

201 - application/json

Contact created successfully

id
string
required

Contact ID

Example:

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

firstName
string
required

The contact's first name

Example:

"John"

lastName
string
required

The contact's last name

Example:

"Doe"

status
enum<string>
required

Contact status

Available options:
UNDEFINED,
APPLIED,
IN_PROCESS,
REJECTED,
ACCEPTED
Example:

"APPLIED"

fields
object
required

The contact's catalogued field values, keyed by field key — the counterpart of metadata, and the only half an agent can speak. Each key is defined in the company catalog (GET /contact-fields) and addressable in a flow as {{contact.<key>}}. Written through fields on POST /contacts and PATCH /contacts/{id}, never through metadata. A field with no value is absent rather than null; {} means none are set.

Example:
createdAt
string<date-time>
required

Created timestamp (UTC)

Example:

"2025-11-20T10:30:00Z"

updatedAt
string<date-time>
required

Updated timestamp (UTC)

Example:

"2025-11-20T10:30:00Z"

jobId
string
deprecated

[Deprecated] Single job ID; use jobIds instead.

Example:

"987e6543-e21b-12d3-a456-426614174000"

jobIds
string<uuid>[]

Jobs the contact is assigned to.

Maximum array length: 50
email
string

The contact's email address

Example:

"john.doe@example.com"

phoneNumber
string

The contact's phone number

Example:

"+421915123456"

gdprExpiryDate
string<date>

GDPR expiry date. Currently returned as a date (no time). NOTE: We plan to migrate to a timestamp with timezone (timestamptz) for global correctness.

Example:

"2026-11-16"

overallRating
number

Overall rating/match score (0-100)

Required range: 0 <= x <= 100
Example:

85

metadata
object

Your own scratch space on the contact: free-form, unvalidated, and never read by an agent. Replaced wholesale on update. Not the place for values you want an agent to say — those are fields.

Example:
analysisCount
number

Number of analyses for this contact. Not currently populated by any endpoint — treat as absent.

Example:

2

interviewCount
number

Number of conversations for this contact. Keeps its original field name, and is not currently populated by any endpoint — treat as absent.

Example:

3

Convenience links to related collections. Endpoints may be added incrementally.

Example:
gender
enum<string> | null

The contact's gender. Used for gender-aware addressing on the call. Null if not explicitly set (auto-detected from the name).

Available options:
male,
female
Example:

"female"

anonymizedCvText
string

The contact's anonymized CV in plain text.

Example:

"[NAME]\nSoftware Engineer\nExperience: ..."

workHistory
object[]

Work history items for the contact.

Maximum array length: 20