> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CDP to CRM object sync: push segments into your CRM as native objects

> Configure a segment-to-object mapping for Salesforce, HubSpot, Braze, Iterable, Customer.io, or Klaviyo, map source traits onto CRM fields with an upsert key, run on-demand syncs, and read the sync-run history.

# CDP to CRM object sync

Object sync activates a CDP segment into your CRM or martech app as **native object writes** — Salesforce Contacts, Leads, or Accounts; HubSpot contacts or companies; Braze, Iterable, Customer.io, or Klaviyo profiles. Records are upserted on an identifier field you choose, on the cadence you set.

This is the reverse-ETL path: the segment you build in the CDP becomes the source, and the CRM object becomes the destination.

## 1. Object sync vs per-event destination dispatch

The integrations [destinations](/guides/cdp-source-and-destinations) panel routes CDP **events** to your connected SaaS apps one event at a time. That is the right shape for streaming new sign-ups and single contact updates.

Object sync exists for the **segment-level batch** case:

* You define a mapping once (which segment, which object type, which fields).
* You run it on demand, or on a schedule you configure.
* Profiles are chunked into upsert batches of 100 records each and dispatched through your connected integration.

Choose object sync when the unit of work is "this segment, into that CRM object," not "this event, into that app."

<Info>
  Connect the destination first from the [destinations](/guides/cdp-source-and-destinations) page — object sync can only run against a destination your organization has connected. No credentials are accepted on the object-sync surface; the connected account stays the only path.
</Info>

## 2. Destinations and object types

Object sync supports six destinations, each with a closed list of native object types you may target:

| Destination   | Object types                 | Default upsert identifier |
| ------------- | ---------------------------- | ------------------------- |
| `salesforce`  | `Contact`, `Lead`, `Account` | `Email`                   |
| `hubspot`     | `contact`, `company`         | `email`                   |
| `braze`       | `user`                       | `external_id`             |
| `iterable`    | `user`                       | `email`                   |
| `customer_io` | `person`                     | `id`                      |
| `klaviyo`     | `profile`                    | `email`                   |

The `object_type` you set is validated against this list when you PATCH your config, so a target the destination cannot accept fails fast at config time, not at run time.

## 3. Configure a mapping

Read the config for every destination with `GET /api/v1/cdp/crm-sync/config`. It returns one entry per destination with its current mapping, the object types it accepts, and the last sync outcome.

To create or update a mapping, PATCH the destination with only the keys you want to change — the merge is scoped to that destination, so every other destination's config is preserved:

```bash theme={null}
curl -X PATCH "https://api.orbit.devotel.io/api/v1/cdp/crm-sync/config/salesforce" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "object_type": "Lead",
    "segment_id": "seg_high_value_clickers",
    "identifier_field": "Email",
    "field_map": {
      "email": "Email",
      "first_name": "FirstName",
      "last_name": "LastName",
      "lifecycle_stage": "LeadStage__c"
    },
    "schedule_minutes": 60
  }'
```

| Field              | Rule                                                                                                    |
| ------------------ | ------------------------------------------------------------------------------------------------------- |
| `enabled`          | Must be `true` before a run is accepted.                                                                |
| `object_type`      | Must be one of the destination's whitelisted object types (above).                                      |
| `identifier_field` | The upsert key the CRM matches on; defaults to the destination's canonical field if you do not set one. |
| `field_map`        | `{ "source_trait": "destination_field" }` pairs. Source accepts dotted trait paths up to 5 segments.    |
| `segment_id`       | The segment this mapping draws from.                                                                    |
| `schedule_minutes` | Optional cadence, 15 to 1440 minutes, for scheduled runs.                                               |

<Warning>
  The `identifier_field` must appear as one of the **values** in your `field_map`. The config is rejected otherwise, because without a mapped key every record would be blind-inserted as a duplicate in your CRM.
</Warning>

## 4. Field-map and identifier rules

Two rules guard the mapping, at config time and again at run time:

1. **The identifier must be a mapped destination field.** A profile can only be upserted if its record carries the upsert key.
2. **No two source traits may write to the same destination field.** A duplicate would silently clobber one of your traits.

At run time, every profile is projected through your field map. A profile whose mapped record has a **missing or empty identifier value is skipped** — never blind-inserted — and counted in the run's `skipped` total.

## 5. Run a sync

Trigger an on-demand run with `POST /api/v1/cdp/crm-sync/run/{destination}`:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/cdp/crm-sync/run/salesforce" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "segment_id": "seg_high_value_clickers",
    "profiles": [
      { "email": "ana@example.com", "first_name": "Ana", "last_name": "Lopez" },
      { "email": "ben@example.com", "first_name": "Ben", "last_name": "Ng" }
    ]
  }'
```

* `segment_id` (required, up to 200 chars) labels which segment the profiles belong to; it is recorded on the run.
* `profiles` is the batch of profiles to project through the stored field map (1 to 5000 per request).

The response is the recorded **run object**:

```json theme={null}
{
  "id": "9c3d...",
  "ts": "2026-09-27T10:04:11Z",
  "destination": "salesforce",
  "object_type": "Lead",
  "segment_id": "seg_high_value_clickers",
  "requested": 2,
  "matched": 2,
  "skipped": 0,
  "batches": 1,
  "dispatched": 1,
  "dispatch_skipped": 0,
  "status": "ok",
  "rows": { "attempted": 2, "delivered": 2, "rejected": 0, "failed": 0 },
  "dispatch_failures": []
}
```

Records are grouped into batches of 100 and upserted through your connected integration. `status` reads:

* `ok` — every batch upserted.
* `partial` — some batches upserted, some failed.
* `failed` — every batch failed.
* `skipped` — nothing to dispatch (no matched records, or no connected account wired yet).

<Info>
  A run against a destination with no connected account is still recorded — every batch reports as `dispatch_skipped` — so you can validate the mapping end to end before you wire the integration.
</Info>

## 6. Read the run history

`GET /api/v1/cdp/crm-sync/runs` returns the most recent runs, newest first. The history is capped at the last 50 runs per organization; older runs roll off as new ones land.

Use it in a [sync-run-log](/guides/cdp-sync-run-log) style loop: shape the run, check `rows.rejected` (profiles skipped for a missing upsert key) and `dispatch_failures` (per-batch provider errors, with record counts — never the profile data) to find the leg that needs a fix.

## 7. Consent and audit

Object sync activates a segment you defined, through a connection you authorized. The decision to sync, and the cadence, are entirely your organization's controls:

* Config changes and runs are written to the audit log with the destination and the operation counts — never the profile records themselves.
* Runs record counts (matched, skipped, batches, dispatch outcomes), not profile payloads. Failed batches keep the provider error message and the batch's record count only.

If you operate under a consent regime, the segment's membership is the filter that decides whose data reaches the CRM — keep the segment definition aligned with your subscribers' consent, as you would for any other activation surface ([erasure propagation](/guides/cdp-erasure-propagation) covers the delete path).

## Related

* [CDP to ERP object sync](/guides/cdp-erp-object-sync) — the sibling surface for NetSuite, SAP, Workday, and QuickBooks
* [CDP destinations (per-event dispatch)](/guides/cdp-destinations)
* [Build the source segment](/guides/cdp-segments)
* [Decode CRM connection and dispatch errors](/troubleshooting/crm-integration-errors)
* [Reverse-ETL warehouse exports](/guides/cdp-reverse-etl-and-warehouse-exports)
