Add or Update Contact
Create new contacts, update existing ones, or upsert with automatic duplicate detection
Creates a new contact, updates an existing contact, or performs an upsert (create if not found, update if found). Supports full contact and entity data, custom fields, and respects your CRM mode (B2B/B2C) settings.
Requires a valid api_key and developer_secret in the JSON request body. Requests must originate from a whitelisted IP address.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
api_key |
body | string |
Yes | Your Zyntro API key |
developer_secret |
body | string |
Yes | Your developer secret (zdk_XXX). Issued during developer onboarding. |
org_id |
body | string |
Yes | Your organization UUID |
member_id |
body | string |
Yes | The member UUID performing the action. Must be a valid member in the organization. |
mode |
body | string |
No | Operation mode: insert (create only, fail if exists), update (update only, fail if not found), or upsert (create or update). Default: upsert |
lookup_by |
body | string |
No | How to find existing contacts in update or upsert mode: email, phone, or contact_id. Default: email |
skip_entity |
body | boolean |
No | Set to true to skip entity creation even in B2B mode. Default: false |
contact |
body | object |
Yes | Contact data object. See Contact Fields below. |
entity |
body | object |
No | Entity (company) data object. See Entity Fields below. In B2B mode, an entity name is auto-generated from the contact name if not provided. |
debug |
body | boolean |
No | Set to true to include debug information in the response. |
Info: All contact and entity field names use camelCase in the request body (e.g., firstName, primaryEmail, roleLabel). The API maps these to the internal database column names automatically.
Contact Fields (contact object)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
firstName |
string | — | Contact first name. Required for insert mode. |
|
primaryEmail |
— | Primary email address. Required for insert mode. Used as the default lookup key in upsert mode. |
||
lastName |
string | — | Contact last name | |
name |
string | — | Full display name. Auto-generated from firstName + lastName if omitted. | |
salutation |
string | — | Salutation (e.g., Mr, Ms, Dr) | |
roleLabel |
string | — | Contact role (e.g., Decision Maker, Influencer) | |
secondaryEmail |
— | Secondary email address | ||
primaryPhone |
string | — | Primary phone number | |
secondaryPhone |
string | — | Secondary phone number | |
address |
string | — | Street address line 1 | |
address2 |
string | — | Street address line 2 | |
city |
string | — | City | |
state |
string | — | State or province | |
zip |
string | — | Postal or ZIP code | |
country |
string | — | Country name | |
countryCode |
string | — | Two-letter country code (e.g., US, CA, GB) | |
timezone |
string | — | IANA timezone (e.g., America/New_York) | |
source |
string | — | How the contact was acquired (e.g., Website Form, API Import) | |
stageId |
string | — | Pipeline stage UUID. Defaults to the organization's default stage if omitted. | |
owner |
string | — | Member UUID of the contact owner. Defaults to the member_id from the request. |
|
status |
string | — | Contact status. Must match a status defined in your CRM settings. Defaults to your org's default contact status. | |
tags |
json | [] | JSON array of tag strings (e.g., ["prospect", "webinar"]) |
|
customFields |
json | {} | JSON object of custom field key-value pairs. Keys must match custom fields defined in your CRM settings. | |
autoEngage |
boolean | — | Whether SI should auto-engage this contact | |
emailOptout |
boolean | Email opt-out flag | ||
smsOptout |
boolean | SMS opt-out flag | ||
dnc |
boolean | Do Not Call flag | ||
isTest |
boolean | Mark as a test contact |
Entity Fields (entity object)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | — | Entity (company) name. Required if providing an entity object. | |
status |
string | — | Entity status. Must match a status defined in your CRM settings. | |
label |
string | — | Entity type label (e.g., Company, Agency, Household) | |
location |
string | — | Entity location | |
website |
url | — | Entity website URL | |
logo |
url | — | Entity logo URL | |
tags |
json | [] | JSON array of tag strings | |
socials |
json | {} | JSON object of social media profiles | |
customFields |
json | {} | JSON object of custom field key-value pairs |
Request Body
Content-Type: application/json
Example
{
"mode": "upsert",
"entity": {
"name": "Acme Corp",
"label": "Company",
"status": "Active",
"website": "https://acme.com"
},
"org_id": "YOUR_ORG_ID",
"api_key": "YOUR_API_KEY",
"contact": {
"tags": [
"prospect",
"enterprise"
],
"source": "API Import",
"status": "Active",
"lastName": "Smith",
"firstName": "Jane",
"roleLabel": "Decision Maker",
"customFields": {
"industry": "Technology",
"company_size": "50-200"
},
"primaryEmail": "[email protected]",
"primaryPhone": "+1-555-0123"
},
"lookup_by": "email",
"member_id": "YOUR_MEMBER_ID",
"developer_secret": "YOUR_DEVELOPER_SECRET"
}
Code Samples
curl -X POST https://app.zyntrohq.com/apis/v2/public/crm/addContactsFull \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_API_KEY",
"developer_secret": "YOUR_DEVELOPER_SECRET",
"org_id": "YOUR_ORG_ID",
"member_id": "YOUR_MEMBER_ID",
"mode": "upsert",
"contact": {
"firstName": "Jane",
"lastName": "Smith",
"primaryEmail": "[email protected]",
"source": "API Import",
"status": "Active"
},
"entity": {
"name": "Acme Corp",
"status": "Active"
}
}'
Response
{
"data": {
"action": "created",
"message": "Contact created successfully",
"entity_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"contact_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"entity_action": "created"
},
"status": "success"
}
{
"data": [
"Missing required contact fields: firstName, primaryEmail"
],
"status": "error"
}
Errors
| Code | Message | Resolution |
|---|---|---|
MISSING_API_KEY |
API key is required The api_key field is missing.
|
Include your API key. |
INVALID_API_KEY |
Invalid API key No active user matches the key. |
Verify your API key in Me > API & Webhooks. |
IP_NOT_WHITELISTED |
IP address not whitelisted The request originated from an IP not in the key's allowed list. |
Contact your client to add your IP to the developer key whitelist. |
INVALID_DEVELOPER_SECRET |
Invalid developer_secret The developer secret does not match any active key. |
Verify your developer secret. If lost, have your client revoke and reissue the key. |
MISSING_DEVELOPER_SECRET |
Missing developer_secret The developer_secret field is missing or empty.
|
Include your developer secret in the request body. This is issued during developer onboarding. |
MISSING_CONTACT |
contact data is required The contact object is missing from the request body.
|
Include a contact object with at least firstName and primaryEmail. |
MEMBER_NOT_FOUND |
Member not found in this organization The member_id does not belong to any member in the specified organization.
|
Use a valid member ID from your organization. |
VALIDATION_ERROR |
Contact validation errors: ... One or more contact fields failed type or format validation. |
Check the error message for specific field issues (invalid email, phone format, exceeds max length, etc.). |
CUSTOM_FIELD_ERROR |
Contact custom field errors: ... A custom field key does not exist or its value is not in the allowed set. |
Verify custom field names and allowed values in your CRM settings. |
NOT_FOUND_UPDATE |
Contact not found for update In update mode, no existing contact matches the lookup criteria.
|
Use upsert mode instead, or verify the contact exists. |
Tip: In upsert mode with lookup_by: "email", the API searches for an existing contact by primaryEmail. If found, it updates the record and merges custom fields (new keys are added, existing keys are overwritten). If not found, it creates a new contact.
Warning: Custom field keys in customFields must exactly match the field tags defined in your CRM settings (Settings > CRM > Custom Fields). Unrecognized keys are rejected. For fields with predefined options, the value must be one of the allowed choices.