409 on createOrUpdate call

Hey everyone! :waving_hand:

I’m running into this really weird issue with HubSpot’s API and I’m hoping someone here has seen this before or can help me understand what’s going on.

## The Situation

I’m trying to use HubSpot’s v1 createOrUpdate API to update an existing contact, but instead of updating it, I’m getting a 409 “Contact already exists” error. This is confusing because… well, that’s exactly what createOrUpdate is supposed to handle, right?

Here’s what I’m doing:

```bash
curl --request POST \
--url https://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/john.doe@example.com \
--header ‘Authorization: Bearer HUBSPOT_API_TOKEN’ \
--header ‘Content-Type: application/json’ \
--data ‘{
“properties”: [
{“property”: “email”, “value”: “john.doe@example.com”},
{“property”: “firstname”, “value”: “John”},
{“property”: “lastname”, “value”: “Doe”},
{“property”: “phone”, “value”: “+1234567890”},
{“property”: “city”, “value”: “New York”},
{“property”: “country”, “value”: “United States”}
]
}’
```

## The Error Response

And here’s what I’m getting back:

```json
{
“message”: “Contact already exists. Existing ID: 123456789”,
“identityProfile”: {
“vid”: 123456789,
“identity”: [
{
“value”: “a1b2c3d4-e5f6-7890-abcd-ef1234567890”,
“type”: “LEAD_GUID”,
“timestamp”: 1759929705399
},
{
“value”: “john.doe@example.com”,
“type”: “EMAIL”,
“timestamp”: 1759930250066,
“isPrimary”: true
},
{
“value”: “j.doe@company.com”,
“type”: “EMAIL”,
“timestamp”: 1759930225502,
“isSecondary”: true
}
],
“linkedVid”: [52408801, 68726151],
“isContact”: true,
“isCanonicalProfile”: true,
“savedAtTimestamp”: 1759930250186,
“mergeInputs”: []
},
“errors”: [
{
“message”: “A contact with email john.doe@example.com already exists. Existing ID: 123456789”,
“in”: “email”
}
],
“status”: “error”,
“correlationId”: “5d9c44a7-6f95-48b2-90aa-03a1f8a201d4”,
“category”: “OBJECT_ALREADY_EXISTS”,
“error”: “CONTACT_EXISTS”
}
```

## What I’ve Tried

I’ve checked and there’s definitely no other account with the original email address - the contact I’m trying to update is unique.

The only workaround I’ve found so far is:
1. Change the email in the HubSpot UI (add an underscore or something)
2. Then make the request with the correct data using the changed email as the URL parameter

But this is obviously not a sustainable solution! :sweat_smile:

## My Questions

1. **Has anyone else seen this behavior?** Is this a known issue with the createOrUpdate API?
2. **Am I missing something obvious?** Maybe there’s a different approach I should be using?
3. **Is there a better way to handle this?** The workaround I found is pretty clunky.

## What I’m Hoping For

Ideally, I’d like to be able to just call the createOrUpdate API and have it work as expected - either create the contact if it doesn’t exist, or update it if it does. That’s the whole point of the API, right?


@NRukosuev wrote:

Hey everyone! :waving_hand:

I’m running into this really weird issue with HubSpot’s API and I’m hoping someone here has seen this before or can help me understand what’s going on.

## The Situation

I’m trying to use HubSpot’s v1 createOrUpdate API to update an existing contact, but instead of updating it, I’m getting a 409 “Contact already exists” error. This is confusing because… well, that’s exactly what createOrUpdate is supposed to handle, right?

Here’s what I’m doing:

```bash
curl --request POST \
--url https://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/john.doe@example.com \
--header ‘Authorization: Bearer HUBSPOT_API_TOKEN’ \
--header ‘Content-Type: application/json’ \
--data ‘{
“properties”: [
{“property”: “email”, “value”: “john.doe@example.com”},
{“property”: “firstname”, “value”: “John”},
{“property”: “lastname”, “value”: “Doe”},
{“property”: “phone”, “value”: “+1234567890”},
{“property”: “city”, “value”: “New York”},
{“property”: “country”, “value”: “United States”}
]
}’
```

## The Error Response

And here’s what I’m getting back:

```json
{
“message”: “Contact already exists. Existing ID: 123456789”,
“identityProfile”: {
“vid”: 123456789,
“identity”: [
{
“value”: “a1b2c3d4-e5f6-7890-abcd-ef1234567890”,
“type”: “LEAD_GUID”,
“timestamp”: 1759929705399
},
{
“value”: “john.doe@example.com”,
“type”: “EMAIL”,
“timestamp”: 1759930250066,
“isPrimary”: true
},
{
“value”: “j.doe@company.com”,
“type”: “EMAIL”,
“timestamp”: 1759930225502,
“isSecondary”: true
}
],
“linkedVid”: [52408801, 68726151],
“isContact”: true,
“isCanonicalProfile”: true,
“savedAtTimestamp”: 1759930250186,
“mergeInputs”: []
},
“errors”: [
{
“message”: “A contact with email john.doe@example.com already exists. Existing ID: 123456789”,
“in”: “email”
}
],
“status”: “error”,
“correlationId”: “5d9c44a7-6f95-48b2-90aa-03a1f8a201d4”,
“category”: “OBJECT_ALREADY_EXISTS”,
“error”: “CONTACT_EXISTS”
}
```

## What I’ve Tried

I’ve checked and there’s definitely no other account with the original email address - the contact I’m trying to update is unique.

The only workaround I’ve found so far is:
1. Change the email in the HubSpot UI (add an underscore or something)
2. Then make the request with the correct data using the changed email as the URL parameter

But this is obviously not a sustainable solution! :sweat_smile:

## My Questions

1. **Has anyone else seen this behavior?** Is this a known issue with the createOrUpdate API?
2. **Am I missing something obvious?** Maybe there’s a different approach I should be using?
3. **Is there a better way to handle this?** The workaround I found is pretty clunky.

## What I’m Hoping For

Ideally, I’d like to be able to just call the createOrUpdate API and have it work as expected - either create the contact if it doesn’t exist, or update it if it does. That’s the whole point of the API, right?


This issue happens because HubSpot’s old v1 createOrUpdate API sometimes gets confused when a contact’s email is linked to more than one internal record. Even though the email looks unique, HubSpot might have duplicate or merged profiles behind the scenes — that’s why you see a 409 Contact already exists error. Basically, the system doesn’t know which record to update. The easiest fix is to stop using the old v1 endpoint and switch to HubSpot’s v3 Contacts API, which handles updates and creations separately without these merge issues. You can first search for the contact by email using the /crm/v3/objects/contacts/search endpoint, then either update it (with a PATCH request) if it exists or create it (with a POST request) if it doesn’t. If you see “linkedVid” values in your error, it means HubSpot has multiple linked records for that same email. In that case, you can manually merge those duplicates using the /contacts/v1/contact/merge-vids/{id} endpoint to clean things up. Once everything’s merged, your createOrUpdate calls will work again — but really, the most reliable and future-proof approach is to use the v3 CRM Contacts API for all contact updates going forward.

Hey @TG6 Thanks for the reply. I agree with your suggestions.

@NRukosuev this is how I think about Legacy Endpoints:

  • It’s stable (it won’t break or be removed without notice)
  • It’s not updated (it’s frozen in time and won’t get new features)
  • It’s not recommended for new development

Best,

Jaycee