# Upsert Contact

POST /api/v2/projects/{projectId}/crm/contacts

Creates a contact, or updates it if one already exists with the same distinct ID. Properties are merged rather than replaced, so omitting a property leaves its current value alone. Send an explicit null to clear one. Omitting `distinctId` generates one with a `soft_` prefix, which is what you want for a contact you only know by email. A new hard distinct ID whose email is already on another contact creates a second, related contact rather than failing. New signups become contacts when the app calls identify() with the user id and email. Use this to import people who already exist. Do not invent people.

## Request

- projectId: string, path, required — ID of the project that owns the contact.
- properties: object, required — Properties to merge. Custom keys are allowed; omitted keys are left unchanged.
  - age: number
  - avatar: string
  - birthday: string
  - createdAt: string
  - description: string
  - displayName: string
  - email: value
  - firstName: string
  - gender: string
  - id: string
  - lastName: string
  - name: string
  - phone: string
  - title: string
  - username: string
  - website: string
- anonymousId: string — Anonymous ID of the browser session this write came from, used to stitch pre-identify events to the contact. Generated when omitted.
- distinctId: string — A database level unique identifier for the contact. If not provided, one will be automatically generated with a soft_ prefix.
- email: string
- toDelete: boolean — Marks the contact for deletion instead of writing properties. Prefer the Delete Contact endpoint.

## Response

- 200: Successful response
  - createdAt: string, required
  - distinctId: string, required
  - id: string, required
  - projectId: string, required
  - properties: object, required
    - $_emailScoreActions: string
    - $_toDelete: boolean
    - $browser: string
    - $browserVersion: string
    - $city: string
    - $continent: string
    - $country: string
    - $countryCode: string
    - $device: string
    - $emailScore: number
    - $emailValidated: boolean
    - $emailValidationResult: string
    - $id: string
    - $initialReferrer: string
    - $initialReferringDomain: string
    - $initialUtmCampaign: string
    - $initialUtmContent: string
    - $initialUtmMedium: string
    - $initialUtmSource: string
    - $initialUtmTerm: string
    - $ip: string
    - $lastEmailValidatedAt: string
    - $lastIdentifiedAt: string
    - $lastSeen: string
    - …
  - toDelete: boolean, required
  - updatedAt: string, required
  - email: string
- 400: Invalid input data
- 401: Authorization not provided
- 403: Insufficient access
- 500: Internal server error
