Skip to content

Contact API - Insert

Use this endpoint to create a new contact in your account, or, with upsert, to create the contact or update it if it already exists.


POST https://e1.envoke.com/v1/contacts

Authentication is via HTTP Basic Auth using your API ID and API Key. The request body must be a single JSON object.

Parameter Type Default Description
key_field string email The field used to check whether the contact already exists: email or remote_id. Any other value is a validation error.
upsert boolean false When 1, true, yes, or on, an existing contact matched by key_field is updated instead of rejected. An empty value (?upsert=) is false.

Field Type Required Description
email string Yes Email address. Required even when key_field=remote_id.
remote_id string With key_field=remote_id Your own identifier for the contact. Max 60 characters.
first_name string No Max 50 characters.
last_name string No Max 50 characters.
title string No Max 50 characters.
company string No Max 80 characters.
phone string No Max 25 characters.
address_1 string No Max 100 characters.
address_2 string No Max 100 characters.
city string No Max 50 characters.
country string No ISO code or country name. Unrecognised values produce a warning.
province string No Max 50 characters.
postal_code string No Max 15 characters.
website string No Max 255 characters.
language string No en or fr (or English, French). Other values produce a warning.
feedback string No Free-text notes. HTML is stripped.
consent_status string No A writable consent type. Defaults to Not Provided.
consent_description string No How consent was obtained. Max 500 characters.
consent_effective_date datetime No When consent was given; not in the future. Defaults to now.
consent_force_downgrade boolean No Has no effect on a new contact. Applies when an upsert updates an existing contact (see Update).
custom_fields object No Values keyed by custom field name.
interests array or object No Tags, subscriptions, and mandatory consent items to set.
autoresponders array or object No Nurture campaigns to add and queue.

id, external_id, date_created, and consent_expiry are read-only and silently ignored. See the Fields reference for accepted values and the full rules for each field.

Note: Custom field values that cannot be stored and nurture campaigns that are skipped for lack of consent are not reported; the contact is still created and the response reports success.


On success, the API returns the new contact in full, in the same shape as a read:

{
"success": true,
"result_text": "The contact was successfully created.",
"result_data": {
"id": "1005",
"external_id": "4b8d2f6a0c1e3a5b7d9f1e3c5a7b9d0f",
"date_created": "2026-10-05T15:20:44+00:00",
"first_name": "Priya",
"last_name": "Nair",
"email": "upsert.target@example.com",
"consent_status": "Not Provided",
"consent_effective_date": "2026-10-05T15:20:44+00:00",
"consent_expiry": "",
"custom_fields": {},
"interests": [],
"autoresponders": []
}
}

(result_data shortened; the response contains every field, with "" for those not set.)

result_data.id is the Envoke contact ID. Store it if you need to update or delete the contact later.

Warnings. An unrecognised country or language value does not fail the request: the field is left empty, and a warning is appended to result_text after a space. Several warnings are joined with . :

The contact was successfully created. 'Narnia' is not a valid value for country; field will not be updated. 'de' is not a valid value for language; field will not be updated

When consent_status is an implied type that does not apply to an EU contact, a consent sentence is appended in the same way (see Consent fields and the example below).


{
"success": false,
"result_text": "Validation failed, see 'result_data' -> 'errors'",
"result_data": {
"errors": [
"Email is required"
]
}
}

Common validation errors:

Error Cause
Email is required email was missing or empty.
Email must be a string email was a number, list, object, or boolean.
Invalid 'key_field' value: 'external_id' key_field is not email or remote_id.
Specified key_field 'remote_id' field was not provided. key_field=remote_id was set but the body has no remote_id.
email value not unique ('alto.lead@example.com' duplicates existing contact with id: '1001') A contact with that email already exists and upsert is not set.
Specified key_field 'remote_id' field value not unique ('HCS-0002' duplicates existing contact with id: '1002') As above, with key_field=remote_id.
Multiple contacts already exist with 'remote_id' value 'HCS-0002'. (IDs: 1002, 1010) More than one contact has the key value. Returned even with upsert. More than 10 matches are shown as a count, e.g. (12 records). Note the two spaces before the bracket.
'nickname' is not a valid contact field The body has a key that is not a contact field.
Invalid 'consent_status': 'Opted In' (possible values: ...) Not a writable consent type. The message lists the valid values.
consent_effective_date cannot be in the future. The effective date is later than now.
Invalid date format for consent_effective_date The effective date could not be read.
Invalid 'custom_field': 'Voice Range' (for a list of valid custom fields try the endpoint '/customfields') No custom field has that name.
'custom_fields' must be an object custom_fields is not an object.
Invalid 'interest': 'Volunteers' (for a list of valid interests try the endpoint '/interests') No tag, subscription, or mandatory consent item has that name.
Invalid 'autoresponder': 'Welcome' (for a list of valid autoresponders try the endpoint '/autoresponders') No nurture campaign has that name.
'interests' must be an object or array interests is a string or number. autoresponders gives 'autoresponders' must be an object or array.

The email, key_field, and duplicate checks run first and return on their own. The remaining body errors in the table are then collected and returned together. Field lengths, email format, and the account’s duplicate check run afterwards and use the field-validation envelope, keyed by database column:

{
"success": false,
"result_text": "Email invalid",
"result_data": {
"fields": {
"email": "Email invalid"
}
}
}
result_text Cause
Email invalid email is not a valid address.
First name must not exceed 50 characters A field is too long. Key fname; see the column-name table for other keys.
contact with email = alto.lead@example.com already exists With key_field=remote_id, the email belongs to another contact and your account checks emails for duplicates. result_data.fields is [].

A missing, empty, or {} body returns {"success":false,"result_data":[],"result_text":"Invalid request"}. Invalid JSON returns Invalid JSON in the same shape.


Create a contact with only an email address

Section titled “Create a contact with only an email address”
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "to.delete@example.com"}' \
"https://e1.envoke.com/v1/contacts"

The contact is created with consent_status Not Provided.

Section titled “Create a full contact with express consent”
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "alto.lead@example.com", "first_name": "Maya", "last_name": "Okafor", "title": "Section Lead", "phone": "416-555-0142", "address_1": "48 Harbour Lane", "address_2": "Unit 5", "city": "Toronto", "province": "ON", "postal_code": "M5V 0Z9", "country": "Canada", "language": "English", "consent_status": "Express", "consent_description": "Signed up at the spring concert", "consent_effective_date": "2026-09-12T18:30:00Z"}' \
"https://e1.envoke.com/v1/contacts"

country is returned as "CA" and language as "en".

Create with custom fields, tags, and a subscription

Section titled “Create with custom fields, tags, and a subscription”
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "alto.lead@example.com", "consent_status": "Express", "custom_fields": {"Voice_Part": "Alto", "Season_Ticket_Number": "HCS-ST-0417"}, "interests": ["Volunteer", "Alumni", "Concert News"], "autoresponders": ["Welcome Sequence"]}' \
"https://e1.envoke.com/v1/contacts"
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "tenor.remote@example.com", "remote_id": "HCS-0002", "first_name": "Daniel", "last_name": "Reyes"}' \
"https://e1.envoke.com/v1/contacts?key_field=remote_id"

The existence check uses remote_id. If your account checks emails for duplicates, an email that belongs to another contact still fails with contact with email = ... already exists.

Caution: the parameter is key_field, not keyfield. An older version of the support article spelled it keyfield. That parameter is ignored without an error, so ?keyfield=remote_id checks for an existing contact by email, and with upsert it updates whichever contact has that email.

Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "alto.lead@example.com"}' \
"https://e1.envoke.com/v1/contacts"
{
"success": false,
"result_text": "Validation failed, see 'result_data' -> 'errors'",
"result_data": {
"errors": ["email value not unique ('alto.lead@example.com' duplicates existing contact with id: '1001')"]
}
}

Upsert: create when the contact does not exist

Section titled “Upsert: create when the contact does not exist”
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "upsert.target@example.com", "first_name": "Priya", "last_name": "Nair"}' \
"https://e1.envoke.com/v1/contacts?upsert=1"

No contact has that email, so it is created: result_text is The contact was successfully created. and result_data is the new contact (see Response).

Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "upsert.target@example.com", "company": "Harbourfront Choral Society", "consent_status": "Express"}' \
"https://e1.envoke.com/v1/contacts?upsert=1"

The same request now matches the existing contact and updates it: result_text is The contact was successfully updated. and result_data is the full, updated contact. An upsert that updates behaves like an update, with these differences:

  • Empty values are ignored and write_empty_values has no effect on POST. A custom field value of "" still deletes that value.
  • A missing consent_status is not defaulted to Not Provided; the contact keeps its current status.
  • id in the body is ignored, so an upsert never matches by id. It matches by email, or by remote_id with key_field=remote_id.
  • Warnings are appended after . , giving a double period: The contact was successfully updated.. 'Narnia' is not a valid value for country; field will not be updated.
Terminal window
curl -X POST -gs \
-u "$API_ID:$API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "paris.member@example.org", "first_name": "Camille", "last_name": "Laurent", "country": "France", "consent_status": "Implied - Inquiry"}' \
"https://e1.envoke.com/v1/contacts"

Implied consent does not apply in the EU region, so the contact is created with Not Provided instead:

{
"success": true,
"result_text": "The contact was successfully created. Their consent status was set to Not Provided because Implied - Inquiry does not apply to contacts in the EU.",
"result_data": {
"id": "1003",
"email": "paris.member@example.org",
"country": "FR",
"consent_status": "Not Provided",
"consent_description": "Implied - Inquiry consent does not apply to contacts in the EU.",
"consent_expiry": ""
}
}

(result_data shortened; the response contains every field.)


Note: The curl examples above are formatted for readability. All requests must use properly encoded URLs.

Full reference, OpenAPI description, and Postman collection: developers.envoke.com.