Skip to main content
POST
Upsert Synced Contact
See the integrations overview for other ways to connect your customer systems with Simple AI. Use this endpoint for Salesforce, Shopify, Zapier, or another system that sends one contact change at a time. The combination of the source key and externalId is the durable sync identity. Send "source": "your_crm" to reuse or create a source. A new source uses that key as its name and custom as its provider; an existing source keeps its metadata. You can also send the object form with key, name, and provider. The first request can define the source, its fields, and the contact in one operation. Later requests can omit fields unless the source schema changes.

Define fields

Field keys use lowercase snake case. Supported types are text, number, boolean, date, enum, url, email, and phone. Define the system field keys before sending their top-level values:
  • name uses text
  • email uses email
  • phone_number uses phone
Each non-system key in attributes must have a field definition owned by the same source. Dates use ISO 8601 datetime strings. Enum values must match one of the field’s enumOptions. A field’s type cannot change and its enum options cannot be removed. To redefine a field, first delete the field.

Apply sparse updates

Omitted contact properties and attribute keys remain unchanged. Send an explicit null to clear a top-level value or attribute. The API does not treat missing contacts as deletions. To remove a contact, use Delete Contact. Use match.email or match.phoneNumber to attach a new external identity to an existing contact. Match values are lookup-only and do not update the contact. A match succeeds only when the value identifies one contact. Ambiguous matches, or email and phone resolving to different contacts, return 409 Conflict. Each organization permits one active contact per phone number and one external identity per source on a contact. A second external ID from the same source that matches that contact returns 409. Use different source keys when two systems have overlapping external IDs. Include a consents array in the same request to record voice or text preferences for the supplied primary phoneNumber:
Nonempty consent updates require an E.164 phone number and both event fields. Each entry uses medium voice or text and category all, transactional, or marketing. An all opt-out covers both categories. optOut: true reports an opt-out; false reports no opt-out from this source; null reports unknown. False does not establish affirmative consent or trigger a provider opt-in. Omit consents, send an empty array, or omit an entry to leave its existing state unchanged. Duplicate medium/category entries and a null consents array are rejected. Any applicable source opt-out blocks the number, including decisions recorded by Simple. Your source can only change its own records. Preferences stay with the number when a contact changes phone numbers. Unclassified outbound calls and texts honor any category’s opt-out. Imported flags are checked by Simple before sending. STOP/START messages and provider opt-out errors also update the consent records. The response includes contact.consents for the contact’s current number. Each entry includes phoneNumber, sourceKey, medium, category, status, and observedAt. The status is opted_out, not_opted_out (your source sent false), unknown (your source sent null), or opted_in (an explicit opt-in recorded by Simple). A null sourceKey identifies a decision recorded by Simple. Any opted_out record blocks the matching communication. A contact without a phone number has an empty consent array.

Retry and order updates

Send eventId and sourceUpdatedAt together to enable deduplication and ordering. They are optional for requests without consent changes. Retry with the same event ID and request body.
  • Accepted updates return applied: true and reason: null.
  • Identical replays return 200, applied: false, and reason: "duplicate".
  • Older timestamps for the same source and external ID return 200, applied: false, and reason: "stale".
  • Conflicting event ID reuse or equal timestamps with different contents return 409.
Skipped requests leave all fields and preferences unchanged and return the current contact with created: false and linked: false. Source, fields, contact values, consent, and event history are saved together. Retrieve the contact later with Get Contact, or use List Contacts with sourceKey and externalId filters.

Use with Zapier

Create a Webhooks by Zapier action that sends a POST request to:
Send Authorization: Bearer YOUR_API_KEY and Content-Type: application/json headers, then map the CRM fields into the request body shown in the example.

Authorizations

Authorization
string
header
required

Your API key, sent as Authorization: Bearer YOUR_API_KEY

Body

application/json
source
required

A source key reuses or creates a custom source. The object form supplies its display name and provider. Existing metadata is preserved when using a string.

Required string length: 1 - 63
Pattern: ^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$
externalId
string
required

Stable contact identifier from the external source.

Required string length: 1 - 1000
fields
object

Field definitions owned by this source. Include them when first used or when labels, descriptions, or enum options change. Define name, email, and phone_number before sending their corresponding top-level values.

name
string | null

Contact name. Omit to leave unchanged or send null to clear it.

Required string length: 1 - 255
email
string<email> | null

Contact email. Omit to leave unchanged or send null to clear it.

phoneNumber
string | null

Contact phone number. Omit to leave unchanged or send null to clear it.

Required string length: 1 - 255
match
Contact Sync Match · object
attributes
object

Values for non-system fields defined by this source. Values must match their field type. Omit a key to leave it unchanged or send null to clear it.

eventId
string

Unique event ID within this source. Supply with sourceUpdatedAt; required for nonempty consents. Retry with the same request body.

Required string length: 1 - 255
sourceUpdatedAt
string<date-time>

Timestamp of the source change, including timezone. Older events for the external identity are skipped. Supply with eventId.

consents
object[]

Preferences for the supplied primary phoneNumber. Omitted or empty leaves consent unchanged. Duplicate medium/category entries are rejected.

Maximum array length: 6

Response

Contact updated or linked, or an event skipped with the current contact returned.

contact
Synced Contact · object
required
created
boolean
required

True when this request created the Simple contact.

linked
boolean
required

True when a new external identity was linked to an existing Simple contact.

applied
boolean
required

False when the request was skipped as a duplicate or stale event.

reason
enum<string> | null
required
Available options:
duplicate,
stale
Last modified on October 6, 2026