Upserts a contact by external reference (create if not exists, update if exists). Can revive a previously deleted contact instead of creating a new one, with optional email matching fallback.

Overview

This endpoint implements idempotent upsert semantics for CRM synchronization. The external reference (namespace + id) acts as the natural key for matching.

Usage Examples

Basic upsert (create or update):

PATCH /api/v1/network/contacts/external/hubspot/12345
Content-Type: application/json

{
  "firstName": "Jane",
  "lastName": "Doe",
  "email": "jane@example.com"
}

Upsert with multiple emails/phones:

{
  "firstName": "John",
  "emails": [
    { "address": "john@work.com", "type": "work" },
    { "address": "john@personal.com", "type": "personal" }
  ],
  "phones": [
    { "number": "+46701234567", "type": "mobile" },
    { "number": "+4687654321", "type": "work" }
  ]
}

Update-only mode (fail if contact doesn't exist):

PATCH /api/v1/network/contacts/external/hubspot/12345?create_only=true

Email fallback matching:

PATCH /api/v1/network/contacts/external/hubspot/12345?match=email

If no contact exists with externalRef {hubspot:12345}, but a contact with the same email exists, that contact is updated and the external reference is added to it.

Response Headers

  • X-Propstreet-Upsert-Action: Indicates the action taken: "created", "updated", or "revived"
  • Location: URI of the resource (only on 201 Created)
  • ETag: Concurrency token for the resource

Reviving a deleted contact

By default (revive=true), if a contact with the external reference was previously deleted, it will be revived and updated with the new data. Set revive=false to create a new contact instead of reviving deleted ones.

Phone Number Normalization

Phone numbers are automatically normalized to E.164 format when possible. Invalid phone numbers are stored as-is without normalization.

Primary Email/Phone Auto-Sync

If you provide emails array but not email, the primary email is automatically set to the first entry. Same applies for phones and phone.

Path Parameters
  • namespace
    Type: string
    required

    External system namespace (e.g., "hubspot", "salesforce", "pipedrive")

  • id
    Type: string
    required

    External ID within the namespace

Query Parameters
  • match
    Type: string

    Fallback matching strategy. Use "email" to match by email if external ref not found.

  • revive
    Type: boolean

    When true (default), revives a previously deleted contact instead of creating a new one.

  • create_only
    Type: boolean

    Only update existing contacts, return 404 if not found

  • validate_only
    Type: boolean

    Validate request without persisting changes

Body·
required

When true, validates the request without persisting changes.

Request body for upserting a contact by external reference. Supports both creating new contacts and updating existing ones.

  • connections
    Type: array object[] ·

    List of company connections (employments).

    A link between a contact and a company, used when creating or updating contacts.

  • email
    Type: null | string
    max length:  
    254

    Primary email address. Also sets the first entry in emails array if not provided.

  • emails
    Type: array object[] ·

    Email addresses with type classification. The first email becomes the primary email if email field is not set.

    Email entry in upsert request.

  • firstName
    Type: null | string
    max length:  
    100

    First name.

  • lastName
    Type: null | string
    max length:  
    100

    Last name.

  • phone
    Type: null | string
    max length:  
    20

    Primary phone number. Also sets the first entry in phones array if not provided.

  • phones
    Type: array object[] ·

    Phone numbers with type classification. The first phone becomes the primary phone if phone field is not set.

    Phone entry in upsert request.

  • primaryLanguage
    Type: null | string
    max length:  
    10

    ISO 639-1 language code (e.g. "en", "sv").

  • strategy
    Type: null | string
    max length:  
    4000

    Investment strategy or description.

  • tags
    Type: array string[]

    List of tags or labels.

Responses
  • application/json
  • application/json
  • application/problem+json
  • application/problem+json
  • application/problem+json
  • application/problem+json
Request Example for patch/api/v1/network/contacts/external/{namespace}/{id}
curl 'https://app.propstreet.com/api/v1/network/contacts/external/{namespace}/{id}?match=&revive=true&create_only=false&validate_only=false' \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
  --data '{
  "firstName": null,
  "lastName": null,
  "email": null,
  "phone": null,
  "emails": [
    {
      "address": "",
      "type": null
    }
  ],
  "phones": [
    {
      "number": "",
      "type": null
    }
  ],
  "primaryLanguage": null,
  "tags": [
    ""
  ],
  "strategy": null,
  "connections": [
    {
      "company": {
        "kind": "existing",
        "id": ""
      },
      "jobTitle": null,
      "primary": true
    }
  ]
}'
{
  "id": "string",
  "uri": null,
  "externalRefs": [
    {
      "namespace": "string",
      "id": "string"
    }
  ],
  "firstName": null,
  "lastName": null,
  "email": null,
  "phone": null,
  "linkedInUrl": null,
  "emails": [
    {
      "address": "string",
      "type": null,
      "isPrimary": true
    }
  ],
  "phones": [
    {
      "number": "string",
      "type": null,
      "isPrimary": true
    }
  ],
  "primaryLanguage": null,
  "tags": [
    "string"
  ],
  "strategy": null,
  "primaryCompanyId": null,
  "companies": [
    {
      "id": "string",
      "uri": null,
      "name": "string",
      "jobTitle": null,
      "isPrimary": true
    }
  ],
  "profilePictureUrl": null,
  "createdUtc": "2026-05-05T17:18:39.880Z",
  "updatedUtc": "2026-05-05T17:18:39.880Z",
  "deletedUtc": null,
  "etag": "string",
  "changeType": null,
  "changeOrigin": null
}