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.
- Type: stringnamespacerequired
External system namespace (e.g., "hubspot", "salesforce", "pipedrive")
- Type: stringidrequired
External ID within the namespace
- Type: stringmatch
Fallback matching strategy. Use "email" to match by email if external ref not found.
- Type: booleanrevive
When true (default), revives a previously deleted contact instead of creating a new one.
- Type: booleancreate
_only Only update existing contacts, return 404 if not found
- Type: booleanvalidate
_only Validate request without persisting changes
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.
- Type: array object[] ·connections
List of company connections (employments).
A link between a contact and a company, used when creating or updating contacts.
- Type: null | stringemailmax length:254
Primary email address. Also sets the first entry in emails array if not provided.
- Type: array object[] ·emails
Email addresses with type classification. The first email becomes the primary email if email field is not set.
Email entry in upsert request.
- Type: null | stringfirst
Name max length:100First name.
- Type: null | stringlast
Name max length:100Last name.
- Type: null | stringphonemax length:20
Primary phone number. Also sets the first entry in phones array if not provided.
- Type: array object[] ·phones
Phone numbers with type classification. The first phone becomes the primary phone if phone field is not set.
Phone entry in upsert request.
- Type: null | stringprimary
Language max length:10ISO 639-1 language code (e.g. "en", "sv").
- Type: null | stringstrategymax length:4000
Investment strategy or description.
- Type: array string[]
List of tags or labels.
- application/json
- application/json
- application/problem+json
- application/problem+json
- application/problem+json
- application/problem+json
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
}