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

# Data sync API (BETA)

> Retrieve and manage data sync accounts and connections.

> [accordion] Required Scopes

The data sync API exposes read and write access to an account's active data sync accounts and connections. It allows you to list and inspect third-party accounts, manage the connection lifecycle, view field mappings, and monitor sync health and errors.

> [!WARNING]
>
> This functionality is currently in beta. By participating in this beta, you agree to HubSpot's [Developer Terms](https://legal.hubspot.com/developer-terms) and [Developer Beta Terms](https://legal.hubspot.com/developerbetaterms). Note that the functionality is still under active development and is subject to change based on testing and feedback.

## Retrieve accounts

To return all third-party accounts connected to the data sync tool in your account, make a `GET` request to `/developers/data-sync/2027-03-beta/accounts`, and include any of the query parameters in the table below:

| Parameter | Type | Description |
| - | - | - |
| `limit` | Integer | Max results per page (default: 100, max: 100) |
| `after` | string | Cursor from paging.next.after |

The response will include the following properties:

| Property | Type | Description |
| - | - | - |
| `id` | String | Unique account identifier |
| `name` | String | Display name of the connected account |
| `accountIdentifier` | String | Human-readable account identifier (e.g. organization URL) |
| `accountInstance` | String | Instance identifier (e.g. Salesforce instance) |
| `status` | String | Connectivity status of the account. Possible values are: <ul><li>`AUTHENTICATED`: authentication is valid and the account is reachable.</li><li>`DISCONNECTED`: the account has been disconnected.</li><li>`REAUTH_REQUIRED`: authentication has expired or has been revoked, and requires re-authentication.</li></ul> |
| `connectionIds` | Array | IDs of connections associated with this account |
| `paging` | Object | An object that contains a `next.after` field that can be used to request more results. |
| `createdAt` | String | An ISO 8601 timestamp denoting when the account was first connected. |

An example response is shown in the code block below:

```json theme={null}
{
  "results": [
    {
      "id": "12345",
      "name": "Salesforce Production",
      "accountIdentifier": "my-org.salesforce.com",
      "accountInstance": "my-org-instance-123",
      "status": "AUTHENTICATED",
      "createdAt": "2024-01-10T08:00:00.000Z",
      "connectionIds": ["1001", "1002"]
    }
  ],
  "paging": {
    "next": { "after": "eyJhZnRlciI6IjEwMCJ9" }
  }
}
```

To retrieve a single account by ID, make a `GET` request to `/developers/data-sync/2027-03-beta/accounts/{accountId}`.

The response will resemble the following:

```json theme={null}
{
  "id": "12345",
  "name": "Salesforce Production",
  "accountIdentifier": "my-org.salesforce.com",
  "accountInstance": "NA-AMAC",
  "status": "AUTHENTICATED",
  "createdAt": "2024-01-10T08:00:00.000Z",
  "updatedAt": "2024-03-15T10:00:00.000Z",
  "connectionIds": ["1001", "1002"]
}
```

## Retrieve connections

To retrieve all connections for the account, make a `GET` request to `/developers/data-sync/2027-03-beta/connections`. You can optionally include the query parameters in the table below to filter the results:

| Parameter | Type | Description |
| - | - | - |
| `accountId` | string | Filter by account |
| `limit` | integer | Max results per page (default: 100, max: 100) |
| `after` | string | Pagination cursor |

The response will include the following properties:

| Property | Type | Description |
| - | - | - |
| `id` | String | Unique connection identifier |
| `accountId` | String | ID of the associated account |
| `objectType` | String | HubSpot object type (e.g. `contacts`) |
| `objectTypeId` | String | HubSpot object type ID (e.g. `0-1`) |
| `remoteObjectType` | String | Object type in the remote app |
| `direction` | String | Sync direction, which can be: `TWO_WAY`, `INBOUND` (remote → HubSpot), or `OUTBOUND` (HubSpot → remote). |
| `status` | String | Connection status. Possible values are: <ul><li>`CREATED`: connection created, but syncing has not yet started.</li><li>`STARTED`: the syncing process has started.</li><li>`PAUSED`: Syncing is currently disabled.</li></ul> |
| `params` | Array | App-specific configuration parameters |
| `paging` | Object | An object that contains a `next.after` field that can be used to request more results. |
| `createdAt` | String | An ISO 8601 timestamp denoting when the account was first connected. |
| `updatedAt` | String | An ISO 8601 timestamp denoting when the account connection status was last updated. |

An example response is shown in the code block below:

```json theme={null}
{
  "results": [
    {
      "id": "1001",
      "accountId": "12345",
      "objectType": "contacts",
      "objectTypeId": "0-1",
      "remoteObjectType": "contacts",
      "direction": "TWO_WAY",
      "status": "STARTED",
      "createdAt": "2024-03-15T10:00:00.000Z",
      "updatedAt": "2024-06-01T14:22:00.000Z",
      "params": [
        {
          "name": "base_id",
          "value": "val123"
        }
      ]
    }
  ],
  "paging": {
    "next": { "after": "eyJhZnRlciI6IjEwMCJ9" }
  }
}
```

To retrieve a single connection by its ID, make a `GET` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}`.

The response will resemble the following:

```json theme={null}
{
  "id": "1001",
  "accountId": "12345",
  "objectType": "contacts",
  "objectTypeId": "0-1",
  "remoteObjectType": "contacts",
  "direction": "TWO_WAY",
  "status": "STARTED",
  "createdAt": "2024-03-15T10:00:00.000Z",
  "updatedAt": "2024-06-01T14:22:00.000Z",
  "params": [
    {
      "name": "base_id",
      "value": "val123"
    }
  ]
}
```

### Retrieve field mappings

To return all field mappings configured for a connection, including which fields are synced and in which direction, make a `GET` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/field-mappings`.

The response will include the following properties:

| Field | Type | Description |
| - | - | - |
| results | Array | A list of configured field mappings. Each entry in the list includes the following properties, detailed in the rows below: <ul><li>`hubspotField`: an object containing details for the field in HubSpot.</li>, <li>`remoteField`: an object containing details for the field in your third-party account.</li><li>`direction`: a string denoting sync direction for this field. Available options are: `TWO_WAY`, `INBOUND`, `OUTBOUND`.</li><li>`status`: a string denoting whether this mapping is active. Options are: `ENABLED` or `DISABLED`.</li></ul> |
| `hubspotField.id` | String | Unique identifier for the field |
| `hubspotField.label` | String | The name of the property as it appears in HubSpot (e.g., Favorite Food). |
| `hubspotField.crmProperty` | String | Name of this property in HubSpot |
| `remoteField.id` | String | Unique identifier for the field |
| `remoteField.label` | String | The name of the property as it appears in the third-party service you're syncing from (e.g., User's Favorite Food). |
| `remoteField.upstreamFieldId` | String | Upstream identifier for the remote field. Not present for composite fields. |

An example response is shown in the code block below:

```json theme={null}
{
  "results": [
    {
      "hubspotField": {
        "id": "firstname",
        "label": "First Name",
        "crmProperty": "firstname"
      },
      "remoteField": {
        "id": "first_name",
        "label": "First Name",
        "upstreamFieldId": "first_name"
      },
      "direction": "TWO_WAY",
      "status": "ENABLED"
    },
    {
      "hubspotField": {
        "id": "email",
        "label": "Email",
        "crmProperty": "email"
      },
      "remoteField": {
        "id": "email_address",
        "label": "Email Address"
      },
      "direction": "INBOUND",
      "status": "ENABLED"
    }
  ]
}
```

### Activate connection

To start the initial sync for a new connection, or trigger a full re-sync, make a `POST` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/activate`.

A successful response will include the `id` of the connection along with a `status` field confirming that the connection is `STARTED`.

```json theme={null}
{
  "id": "1001",
  "status": "STARTED"
}
```

### Disable connection

To pause syncing for a connection, make a `POST` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/disable`. The connection can be re-enabled at any time.

A successful response will include the `id` of the connection along with a `status` field confirming that the connection is `PAUSED`.

```json theme={null}
{
  "id": "1001",
  "status": "PAUSED"
}
```

### Enable connection

To resume a paused connection, make a `POST` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/enable`:

A successful response will include the `id` of the connection along with a `status` field confirming that the connection has switched back to `STARTED`.

```json theme={null}
{
  "id": "1001",
  "status": "STARTED"
}
```

If you attempt to enable a connection that was never initially activated, you'll receive a `400 VALIDATION ERROR`.

### Retrieve connection health

To retrieve a compact snapshot of the connection's current health, you can make a `GET` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/health`. This endpoint provides a health snapshot and is distinct from the connection status returned by the other connection endpoints.

The response will include the following properties:

| Property | Description |
| - | - |
| `syncStatus` | Current activity state. Consult the `syncStatus` values table below. |
| `recordHealth` | Aggregate record health. Available `recordHealth` values are listed below. |
| `records.inSync` | Number of records currently syncing. |
| `records.failing` | Number of records currently failing to sync. |
| `records.excluded` | Number of excluded records. |

`syncStatus` values:

| Value | Meaning |
| - | - |
| `INITIALIZING` | The connection has started, but there is no usable current activity data yet. |
| `SYNCING` | An initial or catch-up sync is in progress. |
| `PAUSED` | The connection is disabled. |
| `LISTENING` | The connection is active and listening for changes. |
| `UNAVAILABLE` | We cannot determine the current activity state. |

`recordHealth` values:

| Value | Meaning |
| - | - |
| `INITIALIZING` | Current-version record state is not available yet |
| `HEALTHY` | Record counts are available and failing record count is 0. |
| `ISSUES` | Record counts are available and failing record count is greater than 0. |
| `UNAVAILABLE` | HubSpot cannot determine current record state counts. |

An example response is shown below:

```json theme={null}
{
  "id": "1001",
  "syncStatus": "LISTENING",
  "recordHealth": "ISSUES",
  "records": {
    "inSync": 120,
    "failing": 2,
    "excluded": 0
  }
}
```

### Retrieve processed records

To retrieve all records being processed by the sync, make a `GET` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/list-records`. You can optionally include the query parameters in the table below to filter the results:

| Parameter | Type | Description |
| - | - | - |
| `status` | String | When present, only include records with this status. See Status options above |
| `window` | Integer | When present, only include records with activity from the specified number of days. |
| `limit` | Integer | Max results per page (default: 100, max: 100) |
| `after` | String | Opaque cursor from `paging.next.after` |

This will include failing and excluded records, ordered by `lastActivityAt` (in descending order). A cursor preserves that order across pages.

The response includes the following properties:

| Field | Description |
| - | - |
| `hubspotObjectId` | HubSpot object identifier |
| `remoteObjectId` | Remote object identifier |
| `name` | Display name of the record: object name, email or id (in that order) |
| `lastActivity` | The last sync action for this record. See below. Only present for `IN_SYNC` records. |
| `lastActivitySource` | Side from which the last sync action was triggered |
| `lastActivityAt` | Timestamp of the last change of this record |
| `status` | See Status options below |
| `error` | See Error details below |

**`status` options**

| Field | Description |
| - | - |
| `IN_SYNC` | Syncing record. |
| `FAILING` | Record failing to sync; this will include an error field. |
| `EXCLUDED` | Record being excluded due to the connection filters. |

**`lastActivity` options**

| Field | Description |
| - | - |
| `CREATED` | Record was created. |
| `UPDATED` | An update was propagated to the record. |
| `MATCHED` | Found a matching record on the other side of the sync. |

**`error` details**

| Field | Description |
| - | - |
| `side` | Which side of the sync failed: `REMOTE` or `HUBSPOT` |
| `categories` | One or more structured error categories. Available `categories` values are detailed in the table below. |
| `message` | Human-readable error message. |

Each result contains at least one object identifier. A failure can have multiple categories.

`categories` values:

| Category | Description |
| - | - |
| `MISSING_REQUIRED_FIELD` | A required field is missing or set to an empty value. |
| `MISSING_REQUIRED_PROPERTY` | A required CRM property is missing or set to an empty value. |
| `MISSING_ASSOCIATED_RECORD` | Object depends on another record not included in the request. |
| `MISSING_ASSOCIATED_INVOICE` | Object requires an associated invoice not included in the request. |
| `ASSOCIATED_CONTACT_WITHOUT_COMPANY` | References an associated contact without a required associated company. |
| `RECORD_LIMIT_EXCEEDED` | Server does not allow creating additional records. |
| `VALUE_ALREADY_TAKEN` | A field value is already in use by another record. |
| `INVALID_FIELD` | A field value fails server-side validation. |
| `INVOICE_DETAILS_UPDATE_NOT_ALLOWED` | Attempt to modify invoice details, which is not allowed. |
| `INVOICE_STATUS_UPDATE_NOT_ALLOWED` | Attempt to modify invoice status, which is not allowed. |
| `INVOICE_DETAILS_NOT_ALLOWED` | Contains additional invoice information, which is not allowed. |
| `FORBIDDEN` | Caller does not have the necessary permissions. |
| `CANCELED_ACCOUNT` | Account has been canceled. |
| `INTERNAL_ERROR` | Internal server error. |
| `DUPLICATE_RECORD` | Record already exists on the server. |
| `UNCATEGORIZED` | Other uncategorized error. |

For example, a response may resemble the following:

```json theme={null}
{
  "results": [
    {
      "hubspotObjectId": "123",
      "remoteObjectId": "abc-123",
      "lastActivity": "CREATED",
      "lastActivityAt": "2026-08-19T08:57:00.000Z",
      "status": "IN_SYNC"
    },
    {
      "hubspotObjectId": "456",
      "remoteObjectId": "abc-456",
      "lastActivityAt": "2026-08-19T08:57:00.000Z",
      "status": "FAILING",
      "error": {
        "side": "REMOTE",
        "categories": ["MISSING_REQUIRED_FIELD"],
        "message": "Email is required"
      }
    }
  ],
  "total": 101,
  "paging": {
    "next": {
      "after": "abc123"
    }
  }
}
```

### Retrieve connection analysis

To retrieve a diagnosis of the connection's current health and possible next actions, make a `GET` request to `/developers/data-sync/2027-03-beta/connections/{connectionId}/analysis`. This analysis is based on sync health record data and cached error log analyses.

The response will include the following properties:

| Property | Description |
| - | - |
| `analysisStatus` | Whether a completed analysis is available. The available options are detailed in the table below. |
| `outcome` | Overall health outcome. Option values are provided in the `outcome` values table below. |
| `findings` | List of individual findings with severity and recommended action. |
| `findings.code` | Structured finding code (e.g. `ACCOUNT_REAUTH_REQUIRED`). |
| `findings.severity` | The severity of the finding. Possible values are: `ERROR` or `WARNING`. |
| `findings.summary` | Human-readable summary of the findings. |
| `findings.recordCount` | Number of affected records (where applicable). |
| `findings.recommendedAction` | Suggested next action. Options are detailed in the `recommendedAction` values table below. |

`analysisStatus` values:

| Value | Meaning |
| - | - |
| `COMPLETE` | Analysis completed successfully. |
| `INITIALIZING` | Analysis not available yet. |
| `UNAVAILABLE` | Something went wrong computing the analysis. |

`outcome` values:

| Value | Meaning |
| - | - |
| `HEALTHY` | No `ERROR` or `WARNING` findings; connection is neither syncing nor paused. |
| `ISSUES` | At least one `ERROR` or `WARNING` finding matched. |
| `IN_PROGRESS` | Connection is syncing and no `ERROR` or `WARNING` finding matched. |
| `PAUSED` | Connection is paused and no `ERROR` or `WARNING` finding matched. |

`recommendedAction` values:

| Value | Meaning |
| - | - |
| `REAUTHENTICATE_ACCOUNT` | Reconnect and reauthorize the connected account. |
| `REVIEW_CONNECTION_SETTINGS` | Review the connection setup and settings. |
| `REVIEW_SYNC_ERRORS` | Review current record-level errors. |
| `WAIT_FOR_RETRY` | Wait for the connection's automatic retry. |
| `WAIT_FOR_SYNC` | Wait for the current sync to finish. |

An example response is shown below:

```json theme={null}
{
  "id": "1001",
  "analysisStatus": "COMPLETE",
  "outcome": "ISSUES",
  "findings": [
    {
      "code": "ACCOUNT_REAUTH_REQUIRED",
      "severity": "ERROR",
      "summary": "Reconnect the connected account to resume syncing.",
      "recommendedAction": "REAUTHENTICATE_ACCOUNT"
    },
    {
      "code": "MISSING_REQUIRED_FIELD",
      "severity": "ERROR",
      "summary": "2 records are currently failing because a required field is missing.",
      "recordCount": 2,
      "recommendedAction": "REVIEW_SYNC_ERRORS"
    }
  ]
}
```
