Create or Update Contact
Create a contact if they don't exist, or update them if they do — matched by email_address, phone_number, or both.
Endpoint
POST https://api.campaignlark.com/v1/contacts/upsert
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
email_address | string | required | Email address used to look up the contact. Always written to the contact record — cannot be changed via this endpoint. Optional when the workspace allows contacts without an email address and a phone_number is provided. |
phone_number | string | optional | Phone number used to look up the contact. Always written to the contact record. Normalized to E.164, so any format resolving to the same number matches the same contact. Requires a workspace field with the phone_number merge tag. |
fields | object | optional | Key-value map of additional field values keyed by merge_tag. email_address is ignored here if provided — use the top-level email_address field instead. phone_number is likewise ignored when the top-level phone_number is provided. |
tags | array of integers | optional | Tag IDs to assign. On full replace, replaces existing tags. On partial update, preserves existing tags if omitted. |
status | string | optional | Contact status. One of SUBSCRIBED, UNSUBSCRIBED, UNCONFIRMED, CLEANED, COMPLAINED. Defaults to SUBSCRIBED if omitted on create or full replace. On partial update, preserves the existing status if omitted. |
sms_status | string | optional | SMS subscription status. One of SUBSCRIBED, UNSUBSCRIBED, CLEANED, BLOCKED. If omitted when updating an existing contact, the current effective SMS status is preserved. If omitted when creating a contact, it is left unset and reported as SUBSCRIBED in responses. |
partial_update | boolean | optional | Defaults to false. If true, only the provided fields are merged into existing data — unspecified fields, tags, and status are preserved. If false, all fields are replaced with the provided values. |
Sample Request
POST https://api.campaignlark.com/v1/contacts/upsert
Content-Type: application/json
{
"email_address": "tywin@example.com",
"phone_number": "+14155550123",
"fields": {
"first_name": "Tywin",
"last_name": "Lannister"
},
"tags": [1, 2],
"status": "SUBSCRIBED",
"sms_status": "UNSUBSCRIBED",
"partial_update": true
}
Response
Success — 200 OK
{
"data": {
"id": "64f1a2b3c4d5e6f7a8b9c0d1",
"status": "SUBSCRIBED",
"sms_status": "SUBSCRIBED",
"score": 0,
"tags": [
{ "id": 1, "name": "newsletter" },
{ "id": 2, "name": "vip" }
],
"data": {
"email_address": "tywin@example.com",
"phone_number": "+14155550123",
"first_name": "Tywin",
"last_name": "Lannister"
},
"statistics": {
"total_opens": 0,
"total_clicks": 0,
"total_bounces": 0,
"total_delivered": 0
},
"created_at": "2024-02-15T10:30:00Z",
"updated_at": "2024-02-15T10:30:00Z"
}
}
Behaviour
| Scenario | Action |
|---|---|
Only email_address given | Looks the contact up by email address. |
Only phone_number given | Looks the contact up by phone number. Allowed when the workspace permits contacts without an email address. |
| Both given | Looks the contact up by either identity. They must resolve to the same contact, or to no contact at all. |
| Contact not found | Creates a new contact. Status defaults to SUBSCRIBED if not provided. |
Contact found, partial_update: false | Replaces all field values with those provided. |
Contact found, partial_update: true | Merges provided fields into existing data. Unspecified fields, tags, and status are preserved. |
Automation Behaviour
- Create path: fires
CONTACT_SUBSCRIBED_PENDINGif status isUNCONFIRMED. No other automation fires for other statuses. - Update path: fires
TAG_ADDED/TAG_REMOVEDif tags change andFIELD_UPDATEDif tracked fields change.
Errors
| Status | Message | Cause |
|---|---|---|
400 | Email address is required for upsert operation. | email_address is missing and the workspace does not allow contacts without an email address. |
400 | A valid email address or phone number is required for upsert operation. | Both email_address and phone_number are missing in a workspace that allows contacts without an email address. |
400 | The email address and phone number belong to different contacts in your workspace. | email_address and phone_number each match a different existing contact. |
400 | The SMS status you provided is not valid. Valid SMS statuses are: SUBSCRIBED, UNSUBSCRIBED, CLEANED, or BLOCKED. | Invalid sms_status value. |
400 | Phone field not found in workspace configuration. | A phone_number was provided but the workspace has no field with the phone_number merge tag. |
400 | phone number is invalid | The provided phone_number could not be parsed, or it has no country code and the phone_number field has no default region. |
400 | We were unable to find a field with the merge_tag <tag> | A key in fields doesn't match any workspace merge tag. |
400 | The status you provided is not valid. | Invalid status value. |
400 | Field '<label>' is required. | A workspace-required field was omitted on a full replace or create. |